【问题标题】:C++ Callback -- How to decouple the call back typeC++回调——如何解耦回调类型
【发布时间】:2011-07-27 18:25:26
【问题描述】:

例如,在下面的伪代码中,B类需要通过B::m_cb成员调用A::Action()。

目标是,如何制作一个通用的非模板回调类,因此“B”不必是模板,“回调”可以包含任何类型的函数签名。

我曾经使用过以前有这样的代码,但现在我找不到那个实现。我只记得:
- “回调”本身不是模板,但它包含成员模板
- 辅助函数模板 make_callback 将实例化 CallBack 对象

谁能指点一下?

Class A
{
public:
   void Action(){//...};
};

class CallBack
{
   //...
   // CallBack it self it is a NOT a template
   // It can wrap member template though
};

class B
{
public:
   void SetCallback(CallBack to){
      m_cb = to;
   }
   void do_something()
   {
      //...
      m_cb.Execute();
      //...
   }

private:
   CallBack m_cb;

};

int main()
{
   A obj1;
   CallBack cb = make_callback(&obj1, &A::Action);
   B obj2;
   obj2.SetCallback(cb);
   //....
   obj2.do_something();
}

这是我从同一个网站获得的示例代码。 我尝试对其进行了一些改进,使其可以容忍任意回调函数的返回类型。但它仍然无法处理任意数量的参数,如第 18 行。另外,T 是指向成员函数的指针,它应该依赖于 C。我不知道如何强制执行。

#include <iostream>
#include <memory>

// INTERNAL CLASSES

class CallbackSpecBase
{
  public:
    virtual ~CallbackSpecBase() {}
    virtual void operator()(...) const = 0;
};

template<class C, class T>
class CallbackSpec : public CallbackSpecBase
{
  public:
    CallbackSpec(C& o, T m) : obj(o), method(m) {}
/*line 18*/    void operator()(...) const { (&obj->*method)(); } // how to pass "..." into method(...)

  private:
    C& obj;
    T method;
};

// PUBLIC API

class Callback
{
  public:
    Callback() {}

    void operator()() { (*spec)(); }

    template<class C, class T>
      void set(C& o, T m) { spec.reset(new CallbackSpec<C, T>(o, m)); }

  private:
    std::auto_ptr<CallbackSpecBase> spec;
};

// TEST CODE

class Test
{
  public:
    void foo() { std::cout << "Working" << std::endl; }
    void bar() { std::cout << "Like a charm" << std::endl; }
};

int main()
{
  Test t;
  Callback c;
  c.set(t, &Test::foo);
  c();
  c.set(t, &Test::bar);
  c();
}

【问题讨论】:

  • 提示:Boost.Bind 已经提供了这种功能,因此您不必自己实现它。
  • Boost.Bind 可以处理任意回调函数签名吗?喜欢任意类型和任意数量的参数?

标签: c++ templates types callback decouple


【解决方案1】:

您正在寻找的是std::function (C++0x) /boost::function。这些使用类型擦除使函数像一流对象一样工作。你可以这样做:

class A
{
public:
    void Action() {//...};
};

class B
{
public:
    template <typename Func>
    void SetCallback(Func func) {
        m_cb = func;
    }

    void do_something() {
        m_cb(); // whatever function
    }

private:
    std::function<void()> m_cb; // anything callable with no arguments
};

int main()
{
    A obj1;
    B obj2;

    obj2.SetCallback(make_callback(&obj1, &A::Action));
    // or:
    obj2.SetCallback(std::bind(&obj1, &A::Action)); // or boost::bind

    obj2.do_something();
}

【讨论】:

  • 感谢您的回复。我刚刚发现一个示例代码可以在标准 c++ 中执行此操作。我仍然不能做的是处理任意函数签名。似乎需要将“...”传递给另一个函数:像这样: void f(...){ g(...); // 将 f() 的参数传递给 g()。知道我该怎么做吗?
  • @John:我不明白你的问题。你需要缩小你想要做的事情并提出一个真实的用例,而不是一直问假设。
  • @John Crane:签名必须在编译时修复。即使使用可变参数模板,您仍然只能在编译时改变它们。 C++ 将不支持运行时变体参数——它们必须是静态类型的。
【解决方案2】:

我已经根据这篇文章实现了回调机制:

http://www.codeproject.com/KB/cpp/CPPCallback.aspx

通过该实现,您可以使用全局函数静态成员函数非静态成员函数作为回调。

本文描述了如何创建委托对象作为所有这些类型的函子。你的class B 可以有一个委托对象作为成员,一个注册回调函数的方法和一个调用它的方法。 class B 和你的回调函数都不需要模板化!委托对象包含一个对象指针(以防非静态成员函数用作回调)和一个指向模板化静态包装函数的指针。在这个包装函数中,存储了指向回调函数的函数指针。它要么直接调用,要么在绑定到传递的对象指针之前调用。

class B
{
  public:
    void SetCallback(CDelegate f_Delegate)
    {
      m_Delegate = f_Delegate;
    }
    static void do_something()
    {
      //...
      m_Delegate();
      //...
    }

  private:
    static CDelegate m_Delegate;
};

更新:

这里也描述了该机制: 5 years later, is there something better than the "Fastest Possible C++ Delegates"?

【讨论】:

    【解决方案3】:

    这并不能真正回答您的问题,因为回调必须是模板,除非您想通过 void* 传递参数(我认为这完全是疯狂的想法)。

    我问了一个类似的问题:What is wrong with this variadic templates example?

    其中一个答案给出了完整的解决方案:

    #include <memory>
    
    template< typename R, typename ... Args >
    class CallbackBase
    {
    public:
        typedef std::shared_ptr< CallbackBase< R, Args... > >
                CallbackPtr;
    
        virtual ~CallbackBase()
        {
        }
        virtual R Call(  Args ... args) = 0;
    };
    
    template< typename R, typename ... Args >
    class FunctionCallback : public CallbackBase< R, Args... >
    {
    public:
        typedef R (*funccb)(Args...);
    
        FunctionCallback( funccb  cb_ ) : 
            CallbackBase< R, Args... >(),
            cb( cb_ )
        {
        }
        virtual ~FunctionCallback()
        {
        }
        virtual R Call(Args... args)
        {
          return cb( args... );
        }
    private:
      funccb cb;
    };
    
    template < typename R, typename ...Args >
    typename CallbackBase< R, Args... >::CallbackPtr
        MakeCallback( R (*cb)(Args...)  )
    {
        typename CallbackBase< R, Args... >::CallbackPtr
            p( new FunctionCallback< R, Args... >( cb )
    );
        return p;
    }
    
    bool Foo_1args( const int & t)
    {
        return true;
    }
    int main()
    {
        auto cbObj = MakeCallback( & Foo_1args );
    }
    

    希望对您有所帮助。如果你讨厌模板,你总是可以使用 typedef。

    【讨论】:

    • 谢谢。我找到了另一个类似的示例代码,但我相信它更好。公共 API 是免费的模板。我仍然缺少的是如何处理回调函数的可变长度参数列表。我将发布该示例代码作为该问题的另一个答案。请检查
    • 我无法在 8 小时内回答我自己的问题。我已将代码粘贴到问题的末尾。请检查。
    猜你喜欢
    • 2018-12-18
    • 2021-12-04
    • 2019-12-14
    • 2021-11-19
    • 1970-01-01
    • 2019-03-27
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多