16

I tried this, and some variations on it:

template<class T>
class Ptr {
public:
    Ptr(T* ptr) : p(ptr) {}
    ~Ptr() { if(p) delete p; }

    template<class Method>
    Method operator ->* (Method method)
    {
        return p->*method;
    }

private:
    T *p;
};

class Foo {
public:
    void foo(int) {}
    int bar() { return 3; }
};

int main() {
    Ptr<Foo> p(new Foo());

    void (Foo::*method)(int) = &Foo::foo;
    int (Foo::*method2)() = &Foo::bar;

    (p->*method)(5);
    (p->*method2)();

    return 0;
}

But it doesn't work. The problem is that I don't really know what to expect as a parameter or what to return. The standard on this is incomprehensible to me, and since Google did not bring up anything helpful I guess I'm not alone.

Edit: Another try, with C++0x: http://ideone.com/lMlyB

Fozi
  • 4,973
  • 1
  • 32
  • 56

3 Answers3

8

The return of operator->* represents a function in the process of being called, with the only missing parts being the parameters. Thus, you must return a functor that invokes the given function on the given object with the given parameters:

// PTMF = pointer to member function
template<class Obj>
struct PTMF_Object{
  typedef int (Obj::*ptmf)(double,std::string); // example signature

  PTMF_Object(Obj* obj, ptmf func)
    : obj_(obj)
    , func_(func)
  {}

  int operator()(double d, std::string str){
    return (obj_->*func_)(d,str);
  }

  Obj* obj_;
  ptmf func_;
};

template<class T>
struct SmartPtr{
  // ...

  PTMF_Object<T> operator->*(PTMF_Object<T>::ptmf func){
    return PTMF_Object<T>(p, func);
  }
  // ...
};

int main(){
  SmartPtr<Foo> pf(new Foo());
  typedef int (Foo::*Foo_ptmf)(double,std::string);
  Foo_ptmf method = &Foo::bar;

  (pf->*method)(5.4, "oh hi");
}

Edit2
Here is an excellent pdf from Scott Meyers on this very topic (it's the only good literature on overloading operator->*, even though it's from 1999).

Edit
Here is one, if your compiler supports variadic templates: http://ideone.com/B6kRF

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • Sounds scary. I'll have to take a look. – Fozi Apr 07 '11 at 20:50
  • @Fozi: Believe me, it is. Though the paper I just can't find has a full implementation in it, and your smart pointer only needs to inherit from a certain base and all `operator->*` functionality will be there. :| – Xeo Apr 07 '11 at 20:53
  • I actually have a Delegate class that might be able to do that, but it's quite heavyweight so I would like to avoid to return it as a temporary. `(*p).*method` is a lot simpler, although I hoped I could support `->*` easily. – Fozi Apr 07 '11 at 21:00
  • @Fozi: If you can use variadic templates, try the one in the edit I just put together. :) And if your compiler doesn't, you need to emulate it by specializing the `PTMF_Object` by hand for the number of arguments. – Xeo Apr 07 '11 at 21:03
  • This looks promising. The problem is we don't use c++0x *yet*. Thanks. – Fozi Apr 07 '11 at 21:12
  • @Fozi: Then try the updated one, you just need to expand it for argument count >= 3. :) – Xeo Apr 07 '11 at 21:17
  • @Fozi: Bam, article found! :) That paper is really really helpful in understanding how that operator works and how to implement it. – Xeo Apr 07 '11 at 21:34
2

I came back to this problem and I found a simple solution:

template<class Ret, class... Args>
auto operator ->* (Ret (T::*method)(Args...)) -> std::function<Ret(Args...)>
{
  return [this, method](Args&&... args) -> Ret {
    return (this->p->*method)(std::forward<Args>(args)...);
  };
}

The implementation of the full test case can be found here.

The only thing I don't like in this solution is the use of std::function. If someone knows a way to return the lambda directly, please let me know. I have a hunch that it might not be possible because of the way lambda types are defined though.

Fozi
  • 4,973
  • 1
  • 32
  • 56
  • Just don't specify a return type. Since you only have a single `return` statement, the return type is automatically deduced to the inner lambda's type. Otherwise, +1 for the solution. :) – Xeo Jan 29 '12 at 23:38
  • @Xeo That only works for lambdas, but I must specify the return type for the operator `->*`, which is a `decltype()`, but the lambda can not be specified there because it's using `this` and besides each lambda instance has its own type. :( – Fozi Jan 30 '12 at 00:50
  • Oh, right, I overlooked something. :( I don't think you'll get around that `std::function`, unfortunately. – Xeo Jan 30 '12 at 01:03
1

IIRC, the return type of operator->* should be an object which is callable as a function.

So you may have to forward the pointer to member function invocation to an object which overloads operator() (a closure), which is especially painful if you don't have variadic templates handy.

If you have C++0X, you have closures and variadic templates and your problem can be solved with a few lines of generic code.

If you don't, you will have to use macro trickery to simulate variadic templates, and this is not fun to do (even if some people think the opposite).

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • If I go the C++0x path, shouldn't p->*method already return the right object? Would an auto return type not be enough? http://ideone.com/lMlyB Again, I'm stuck. – Fozi Apr 07 '11 at 20:49
  • @Fozi: Very good point. Maybe. I'm not C++0x savvy enough, bud I'd say yes, that would do the trick. Or maybe `decltype` would work too. – Alexandre C. Apr 07 '11 at 21:29