2

I've been going nuts trying to figure this out. Consider the following code (I'm assuming forward references have been defined):

// Signature representing a pointer to a method call
typedef
   void (MyClass::*MyMethod)(int);

class MyClass
{
    MyClass();
    void method1(int i);
    void method2(int i);

    void associateMethod(int index, MyMethod m);
} 

Given the above, the constructor can do things like the following:

MyClass::MyClass()
{
   associateMethod(1, &MyClass::method1);
   associateMethod(2, &MyClass::method2);
}

However, I'd like to be able to invoke 'associateMethod' where the second parameter is an anonymous method. However, the following doesn't compile.

associateMethod(3, [this](int) -> void { /* some code here */ }

I get an error about their being no viable conversion from the lambda to MyMethod.

I'm wondering if the lambda syntax needs to include 'MyClass' somewhere but random guesses for the lambda expression such as

 MyClass::[this](int) -> void {}

or

 [this]&MyClass::(int) -> void {}

don't compile.

Would appreciate any pointers (no pun intended)

Thanks

David
  • 5,991
  • 5
  • 33
  • 39
  • 1
    Why not use a `std::function` instead of a raw function pointer in 1st place? – user0042 Dec 02 '17 at 04:40
  • 2
    Whatever you're trying to do here, it cannot be done this way. Class methods require an instance of a class in order to invoke them. That's what makes them class methods. A lambda is not tied to any instance of any class. A lambda is an anonymous class in of itself. The lambda function is always tied to an instance of a unique, anonymous class. Maybe you'd like to share what real problem you're trying to solve. No, not the one about using lambdas this way, but whichever problem to which you believe the solution is to use a lambda this way. – Sam Varshavchik Dec 02 '17 at 04:41
  • It is difficult to give a specific answer because you don't give working code that produces a specific error. But `using MyMethod = std::function` may be a solution. – Galik Dec 02 '17 at 04:49
  • Hmmm, has the terminology changed since I last used C++ (numerous years ago)? I was under the impression that instance member functions require an object instance but class methods (defined using static) do not require a class instance. However, your sentences, "[a] lambda is not tied to any instance of any class. A lambda is an anonymous class in of itself." actually clears things up for me completely." – David Dec 02 '17 at 13:15
  • What I'm actually trying to do is register selected methods of a class so that they can be called later but I had hoped I could use "in-place" anonymous code (i.e, lambdas) interchangeably. – David Dec 02 '17 at 13:20

2 Answers2

5

You can't convert a lambda expression to a class member function pointer and there's no valid syntax to make it look like one1.

Instead of a raw function pointer, you should declare the MyMethod as std::function signature (as was mentioned in the comments):

using MyMethod = std::function<void(int)>;

You can use lambdas then to initialize this parameter then:

MyClass::MyClass()
{
   associateMethod(1, [this](int a) { this->method1(a); });
   associateMethod(2, [this](int a) { this->method2(a); });
}

1)Lambda functions can be thought as compiler generated callable classes, which take the captures as parameters on construction and provide a operator()() overload with the parameters and body you specify. Hence there's no possible valid conversion to a raw or member function pointer.

user0042
  • 7,917
  • 3
  • 24
  • 39
  • Thanks for all the answers. I am not trying to call the member functions (method1, method2) from a lambda expression. Rather, I want a way to register methods (such as method1, method2) so that they can be invoked later via callbacks and I'd like to be able to register "anonymous" methods as well. I realize I can work around this by making the methods static and passing an object reference as the first parameter but then I have to deal with issues such as access to private fields in the class. – David Dec 02 '17 at 13:08
  • As for std::function, but other than cleaner syntax (certainly a good thing) I was not aware that it behaved any differently than the traditional typedef approach. – David Dec 02 '17 at 13:10
1

user0042's answer seems the way to go but, just for completeness sake, it's worth mentioning that in C++17 captureless lambdas have a constexpr conversion operator to their function pointer type, hence you should(*) be able of converting such a lambda to a member function pointer, via something like:

// ...
void associateMethod(int index, MyMethod m);

template<typename F>
void associateMethod(int index, F m) {
  associateMethod( index,
    static_cast<MyMethod>(
      &MyClass::bindFun< static_cast<void(*)(MyClass*,int)>(m) >
    ) );
}

private:

template<auto F>
void bindFun(int x){ (*F)(this,x); }

// to be used like 
x.associateMethod(0,[](MyClass* this_, int x){ this_->method1(x+1); });

(*) sadly, this compiles in clang but gcc refuses to compile it (I'm going to ask a question about this, you can find it here).

Massimiliano Janes
  • 5,524
  • 1
  • 10
  • 22
  • Interesting, except I'm not trying to invoke method1 from a lambda expression. I wanted to be able to register both member functions and lambda expressions so that either kind of thing could be called back later. – David Dec 02 '17 at 13:22
  • @David, yes this does allow to do that in theory; I called this_->method1 just to give an example ... – Massimiliano Janes Dec 02 '17 at 13:26
  • Not sure why this got zero votes as it answers the question I asked. I can't leverage that answer at this time (we're still on C++14) but intellectually it's good to know how it would be done. – David Dec 03 '17 at 16:37
  • @david BTW, I confirm this is valid C++17 code, although only clang>=5.0 supports it at the moment (gcc has a bug, see the link in the answer for more details). – Massimiliano Janes Dec 03 '17 at 16:40