通过lambda表达式作为成员函数指针在C + +(pass lambda expression a

2019-09-21 02:20发布

我有期望的对象和一个成员函数指针(回调),像这样的框架功能:

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

我如何传递一个lambda表达式呢? 想做somethink是这样的:

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

这一切的意义是不是杂乱的MyObject类的接口与回调。

UPD我可以提高do_some_work绝不是因为我不控制框架,因为它实际上不是一个函数,这儿有数百人。 整个框架是基于该类型的回调。 没有lambda表达式常见的用法例如:

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
    }
};

这里的解决方案是基于对马塞洛的回答我的解决方案:

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));
    }
};

由于我们创建CallbackWrapper我们可以控制它的生命周期对于其中回调asynchonously使用的情况。 谢谢大家。

Answer 1:

这是不可能的。 该构建体(optr->*fptr)()要求FPTR是一个指针到构件。 如果do_some_work是你的控制之下,将其更改为拿东西,与lambda函数是兼容的,如std::function<void()>或参数化类型。 如果它是一个传统的框架,是不是你的控制之下,你可以把它包起来,如果它是一个函数模板,例如:

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

然后,你可以实现一个包装模板:

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... */ });
    }
};

编辑:如果do_some_work异步完成,你必须分配lamf在堆上。 我相应的修改上面的代码,只是为了安全起见。 感谢@大卫·罗德里格斯指出这一点。



Answer 2:

还有更深层次的问题,您正试图采取比语法错配的方法。 如DeadMG表明,最佳解决方案是提高的界面do_some_work采取某种(的算符std::function<void()>在C ++ 11或升压,或甚至一个通用F在其上operator()叫做。

通过马塞洛提供的解决方案解决了语法不匹配,但是因为该库由指针采取的第一个元素,这是调用者的责任,以确保在执行回调时,对象将是活着的。 假设回调是异步的,与他的溶液(和其它类似的替代品)的问题在于,在执行回调之前对象可以潜在被破坏,从而导致未定义的行为。

我会建议你使用某种形式的plimp成语中,其中在这种情况下,目标将是隐藏回调的需要(因为实现的其余部分可能并不需要隐藏,你可以只使用另一个类来处理回调,但它存储由值,如果你不想做必须动态地分配更多的内存):

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 );
   }
};

在这个设计中,两个班分开,而是代表一种独特的单一实体,所以这是很好添加好友声明,让MyClassCallbacks访问内部数据MyClass (两者都是一个单一的实体,只划分提供一个简洁的界面但耦合已经很高了,所以通过添加需要选用额外的耦合friend是没问题的)。

因为之间存在1-1关系MyClassMyClassCallbacks情况下,它们的寿命也必然和就不会有寿命的问题,除了破坏期间。 在破坏你必须确保没有回调注册可踢,而MyClass对象被销毁。

既然你是怎么做,你可能要多走一英里,并做了适当的平普尔 :将所有由指针保存的数据和实施成不同的类型,并提供MyClass存储指针,并提供公正公开功能,实现为转发到PIMPL对象。 这可能是你使用的继承和PIMPL方法是有点麻烦的类型层次结构来实现(如果需要扩展以某种方式棘手MyClass ,从获得Object平普尔对象可以做,而不是接口类型)。



Answer 3:

我不认为你可以做到这一点。 你do_some_work()被声明为接受指针类的方法Object ,所以这样应该提供。 否则optr->*fptr是无效的,因为拉姆达是不是成员Object 。 也许你应该尝试使用std::function和加入所需的成员Object在其关闭。



Answer 4:

您必须使用std::function<void()> 。 两个函数和成员函数指针是高度不适合被回调。



文章来源: pass lambda expression as member function pointer in c++