简体   繁体   中英

pass lambda expression as member function pointer in c++

I have a framework function which expects an object and a member function pointer (callback), like this:

do_some_work(Object* optr, void (Object::*fptr)()); // will call (optr->*fptr)()

How can I pass a lambda expression to it? Want to do somethink like this:

class MyObject : public Object
{
    void mystuff()
    {
        do_some_work(this, [](){ /* this lambda I want to pass */ });
    }
};

The meaning of it all is to not clutter the interface of MyObject class with callbacks.

UPD I can improve do_some_work in no way because I don't control framework and because actually it isn't one function, there're hundreds of them. Whole framework is based on callbacks of that type. Common usage example without lambdas:

typedef void (Object::*Callback)();
class MyObject : public Object
{
    void mystuff()
    {
        do_some_work(this, (Callback)(MyClass::do_work));
    }
    void do_work()
    {
        // here the work is done
    }
};

SOLUTION Here's my solution based on Marcelo's answer:

class CallbackWrapper : public Object
{
    fptr fptr_;
public:
    CallbackWrapper(void (*fptr)()) : fptr_(fptr) { }
    void execute()
    {
        *fptr_();
    }
};

class MyObject : public Object
{
    void mystuff()
    {
        CallbackWrapper* do_work = new CallbackWrapper([]()
        {
           /* this lambda is passed */
        });
        do_some_work(do_work, (Callback)(CallbackWrapper::execute));
    }
};

Since we create the CallbackWrapper we can control it's lifetime for the cases where the callback is used asynchonously. Thanks to all.

This is impossible. The construct (optr->*fptr)() requires that fptr be a pointer-to-member. If do_some_work is under your control, change it to take something that's compatible with a lambda function, such as std::function<void()> or a parameterised type. If it's a legacy framework that isn't under your control, you may be able to wrap it, if it's a function template, eg:

template <typename Object>
do_some_work(Object* optr, void (Object::*fptr)());

Then, you can implement a wrapper template:

template <typename F>
void do_some_work(F f) {
    struct S {
        F f;
        S(F f) : f(f) { }
        void call() { f(); delete this; }
    };
    S* lamf = new S(f);
    do_some_work(lamf, &S::call);
}

class MyObject // You probably don't need this class anymore.
{
    void mystuff()
    {
        do_some_work([](){ /* Do your thing... */ });
    }
};

Edit: If do_some_work completes asynchronously, you must allocate lamf on the heap. I've amended the above code accordingly, just to be on the safe side. Thanks to @David Rodriguez for pointing this out.

There are deeper problems with the approach that you are trying to take than the syntactical mismatch. As DeadMG suggests, the best solution is to improve the interface of do_some_work to take a functor of some sort ( std::function<void()> in C++11 or with boost, or even a generic F on which operator() is called.

The solution provided by Marcelo solves the syntactical mismatch, but because the library takes the first element by pointer, it is the responsibility of the caller to ensure that the object will be alive when the callback is executed. Assuming that the callback is asynchronous, the problem with his solution (and other similar alternatives) is that the object can potentially be destroyed before the callback is executed, causing undefined behavior.

I would suggest that you use some form of plimp idiom, where the goal in this case would be to hide the need for callbacks (because the rest of the implementation might not need to be hidden you could use just another class to handle the callbacks but store it by value, if you don't want do have to dynamically allocate more memory):

class MyClass;
class MyClassCallbacks {
   MyClass* ptr;
public:
   MyClassCallbacks( MyClass* ptr ) : ptr(ptr) {}
// callbacks that execute code on `ptr`
   void callback1() {
      // do some operations
      // update *ptr
   }
};
class MyClass {
   MyClassCallbacks callbackHandler;
public:
   void mystuff() {
      do_some_work( &callbackHandler, &MyClassHandler::callback1 );
   }
};

In this design, the two classes are separated but represent a unique single entity, so it is fine to add a friend declaration and let MyClassCallbacks access the internal data in MyClass (both of them are one single entity, divided only to provide a cleaner interface, but coupling is already high, so adding the extra coupling requiered by friend is no problem).

Because there is a 1-1 relationship between MyClass and MyClassCallbacks instances, their lifetimes are bound and there would be no lifetime issues, except during destruction. During destruction you must ensure that there is no callback registered that can kick in while the MyClass object is being destroyed.

Since you are at it, you might want to walk the extra mile and do a proper pimpl : move all of the data and implementation into a different type that is held by pointer, and offer a MyClass that stores a pointer and offers just the public functions, implemented as forwarders to the pimpl object. This could be somehow tricky as you are using inheritance, and the pimpl idiom is a bit cumbersome to implement on type hierarchies (if you need to extend MyClass , deriving from Object could be done in the pimpl object, rather than the interface type).

I don't think you can do that. Your do_some_work() is declared to accept pointer to methods of class Object , so such should be provided. Otherwise optr->*fptr is invalid since the lambda is not member of Object . Probably you should try using std::function and adding the needed members of Object in its closure.

You must use std::function<void()> . Both function and member function pointers are highly unsuited to being callbacks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM