c++ - C++11/14/17 中指向方法回调的指针?

标签 c++ c++11 c++14 c++17 stdbind

(我之前的一个问题有类似的措辞和例子,但问的是完全不同的东西。 在我询问方法的想法之前。现在我问的是如何获得特定的工作方法。)

我有一些订阅功能,它会在发生某些事情时调用我的回调。 (假设它是一个计时器,并且会在经过一定的毫秒数后向我传递一个对象。)在查看了 lambda、std:functionstd:bind 之后,我认为指向方法的指针的解决方案性能更高且更易于编写(尤其是对于订阅者而言),但我不太明白最后一点。

这个例子有点反射(reflect)了我的项目:我们有一个框架,用 Foo 表示,它只写一次,我们会有很多子类在这里用 Bar 表示,它们将由具有更多领域知识但较少 C++ 知识的人编写.因此,我们希望对 SubscribeTimer() 的调用尽可能简单。最后,该应用程序是高性能的,我们希望消除堆使用,包括创建隐式 std::bind 对象等。

#include <iostream>
#include <functional>
using namespace std;

class Data { int i; };

class Foo {
public:
    typedef void (Foo::*Timer_T)( Data* pd );
    virtual void SubscribeTimer( int iMilliseconds, Timer_T pmethod );
    virtual void SubscribeTimer( int iMilliseconds, std::function<void(Data*)> pfn ); // undesired

    virtual void OnTimerA( Data* pd ) { cout << "Foo::OnTimerA called" << endl; };
};

void Foo::SubscribeTimer( int iMilliseconds, Timer_T pmethod ) {
    Data d;
    (this->*pmethod)( &d );
}

void Foo::SubscribeTimer( int iMilliseconds, std::function<void(Data*)> pfn ) { // undesired
    Data d;
    pfn( &d );
}

class Bar: public Foo {
    public:
    void Init();
    virtual void OnTimerA( Data* pd ) { cout << "Bar::OnTimerA called" << endl; };
    virtual void OnTimerB( Data* pd ) { cout << "Bar::OnTimerB called" << endl; };
};

void Bar::Init() {
    // Works like I want it to: easy to subscribe, and high performance.
    SubscribeTimer( 1000, &Foo::OnTimerA );
    // What I'd like to do, but doesn't work.
    //SubscribeTimer( 1000, &Bar::OnTimerB );
    // Does exactly what I want except more complicated to write and I believe slower to run.
    SubscribeTimer( 1000, std::bind( &Bar::OnTimerB, this, std::placeholders::_1 ) );
}


int main( int nArg, const char* apszArg[] ) {
    Bar bar;
    bar.Init();
}

正如预期的那样(如果您忽略了在 Init() 的调用中编写 Foo:: 而不是 Bar:: 的要求SubscribeTimer()) 程序输出:

Bar::OnTimerA called  (from the version of SubscribeTimer() I like)
Bar::OnTimerB called  (from the version of SubscribeTimer() I think is too verbose/slow)

所以这个例子完美地工作并且做了我需要的......除了子类只能为父类(super class)想定义的方法名称注册处理程序,无论它们是否被定义。但实际上,子类可能希望为不同的事件注册许多处理程序,这些处理程序的名称是父类(super class)不知道的。

所以一句话:我如何将OnTimerB() 传递给SubscribeTimer() 的方法指针版本?我很乐意更改 Timer_T 定义、SubscribeTimer() 或其他内容。不过作为底线,子类的成员指针解决方案比std::function实现更复杂,而且比std::function 实现。另一方面,父类(super class) 中增加的复杂性不是问题,因为它是一次性代码。

最佳答案

调用Bar::OnTimerB , 你需要一个 Bar 类型的指针.因此,您需要实现某种类型删除。

class Foo {
public:
  template <typename Derived>
  void SubscribeTimer(int iMilliseconds, void (Derived::*pmethod)(Data* pd)) {
    auto const self = static_cast<Derived*>(this);
    Data d;
    (self->*pmethod)(&d);
  }
};

显然,如果稍后要调用回调,则需要以某种方式存储它。这就是std::function<>为你抽象。你当然可以自己做,但是std::function<>还不错,尤其是在具有小功能优化的平台上。

因此您可以执行 std::function内部包装:

  template <typename Derived>
  void SubscribeTimer(int iMilliseconds, void (Derived::*pmethod)(Data* pd)) {
    SubscribeTimer(iMilliseconds,
        [self = static_cast<Derived*>(this), pmethod](Data* pd) {
            (self->*pmethod)(pd);
        });
  }

关于c++ - C++11/14/17 中指向方法回调的指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59046383/

相关文章:

c++ - bullseye 代码覆盖率浏览器没有所有的源文件?

c++ - 我的哈希键类型和函数有什么问题?

function - std::vector 的 std::function

c++ - std::atomic 可以用减量抵消增量吗?

c++ - 关于 noexcept 参数

c++ - 调试 400/400 = -858993460

c++ - new char 实际上是否保证类类型的对齐内存?

c++ - 检查模板函数是否存在

c++ - 将整个文件读入线 vector 的最有效方法

c++ - std::chrono::from_stream 正确用法