【问题标题】:Object-Oriented Callbacks for C++?C ++的面向对象回调?
【发布时间】:2011-04-01 00:18:06
【问题描述】:

是否有一些库可以让我在 c++ 中轻松方便地创建面向对象的回调?

例如,埃菲尔语言有“代理”的概念,它或多或少像这样工作:

class Foo{
public:
    Bar* bar;

    Foo(){
        bar = new Bar();
        bar->publisher.extend(agent say(?,"Hi from Foo!", ?));
        bar->invokeCallback();
    }

    say(string strA, string strB, int number){
        print(strA + " " + strB + " " + number.out);
    }

}

class Bar{
public:
    ActionSequence<string, int> publisher;

    Bar(){}

    invokeCallback(){
        publisher.call("Hi from Bar!", 3);
    }
}

输出将是: 你好,来自酒吧! 3 Foo 你好!

所以 - 代理允许将成员函数封装到一个对象中,给它一些预定义的调用参数(来自 Foo 的 Hi),指定开放参数(?),并将它传递给其他可以调用它的对象稍后。

由于 c++ 不允许在非静态成员函数上创建函数指针,因此在 c++ 中实现一些易于使用的东西似乎不是那么简单。我在谷歌上找到了一些关于 C++ 中面向对象回调的文章,但是,实际上我正在寻找一些我可以简单地导入的库或头文件,它们允许我使用一些同样优雅的语法。

有人给我一些建议吗?

谢谢!

【问题讨论】:

  • 我不确定我是否完全理解您的语法,但boost::bind 可用于将具有适当接口的函数和成员函数打包到对象中。 boost.org/doc/libs/1_44_0/libs/bind/…
  • 嘿!是的,我认为这就是我正在寻找的。不幸的是,我无法为 iPhone 编译 boost(在 iPhone 上开发)。我读到这也可能与stl有关。有人可以解释一下吗?

标签: c++ oop callback member-function-pointers eiffel


【解决方案1】:

人们通常使用以下几种模式之一:

继承。也就是说,您定义了一个包含回调的抽象类。然后你拿一个指针/引用它。这意味着任何人都可以继承和提供这个回调。

class Foo {
    virtual void MyCallback(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo> ptr;
    void something(...) {
        ptr->MyCallback(...);
    }
    Base& SetCallback(Foo* newfoo) { ptr = newfoo; return *this; }
    Foo* GetCallback() { return ptr; }
};

再次继承。也就是说,你的根类是抽象的,用户从它继承并定义回调,而不是有一个具体的类和专用的回调对象。

class Foo {
    virtual void MyCallback(...) = 0;
    ...
};
class RealFoo : Foo {
    virtual void MyCallback(...) { ... }
};

更多的继承-静态。这样,您可以使用模板来更改对象的行为。它类似于第二个选项,但在编译时而不是在运行时工作,这可能会产生各种好处和缺点,具体取决于上下文。

template<typename T> class Foo {
    void MyCallback(...) {
        T::MyCallback(...);
    }
};
class RealFoo : Foo<RealFoo> {
    void MyCallback(...) {
        ...
    }
};

您可以获取和使用成员函数指针或常规函数指针

class Foo {
    void (*callback)(...);
    void something(...) { callback(...); }
    Foo& SetCallback( void(*newcallback)(...) ) { callback = newcallback; return *this; }
    void (*)(...) GetCallback() { return callback; }
};

有函数对象——它们重载了operator()。您将想要使用或编写一个功能性包装器——目前在 std::/boost:: 函数中提供,但我也会在这里演示一个简单的包装器。它类似于第一个概念,但隐藏了实现并接受了大量其他解决方案。我个人通常使用它作为我选择的回调方法。

class Foo {
    virtual ... Call(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo> callback;
    template<typename T> Base& SetCallback(T t) {
        struct NewFoo : Foo {
             T t;
             NewFoo(T newt) : t(newt) {}
             ... Call(...) { return t(...); }
        };
        callback = new NewFoo<T>(t);
        return this;
    }
    Foo* GetCallback() { return callback; }
    void dosomething() { callback->Call(...); }
};

正确的解决方案主要取决于上下文。如果您需要公开 C 风格的 API,那么函数指针是唯一的方法(请记住 void* 用于用户参数)。如果您需要在运行时进行更改(例如,在预编译库中公开代码),则此处不能使用静态继承。

简单说明一下:我手动编写了该代码,因此它并不完美(例如函数的访问修饰符等)并且可能存在一些错误。这是一个示例。

【讨论】:

    【解决方案2】:

    在 C++ 中有很多可能性,问题通常是语法之一。

    • 您可以在不需要状态时使用指向函数的指针,但语法真的很糟糕。这可以与 boost::bind 结合使用,以获得更...有趣的...语法 (*)
    • 我纠正了你的错误假设,指向成员函数的指针确实是可行的,语法太尴尬你会跑掉(*)
    • 您可以使用 Functor 对象,基本上 Functor 是一个重载 () 运算符的对象,例如 void Functor::operator()(int a) const;,因为它是一个具有状态的对象,并且可能派生自一个通用接口
    • 如果您不想走运算符重载的道路,您可以简单地创建自己的层次结构,并为回调函数使用更好的名称
    • 最后,您可以利用 C++0x 工具:std::function + lambda 函数在表现力方面确实很棒。

    我希望能对 lambda 语法进行评论;)

    Foo foo;
    std::function<void(std::string const&,int)> func =
      [&foo](std::string const& s, int i) {
        return foo.say(s,"Hi from Foo",i);
      };
    
    func("Hi from Bar", 2);
    func("Hi from FooBar", 3);
    

    当然,func 仅在 foo 可行时才可行(范围问题),您可以使用 [=foo] 复制 foo 以指示按值传递而不是按引用传递。

    (*)Mandatory Tutorial on Function Pointers

    【讨论】:

      【解决方案3】:

      加入仿函数的想法 - 在注册之前使用 std::tr1::function 和 boost::bind 将参数构建到其中。

      【讨论】:

        【解决方案4】:

        在 C++ 中使用回调的最 OO 方式是调用接口的函数,然后传递该接口的实现。

        #include <iostream>
        
        class Interface
        {
          public:
          virtual void callback() = 0;
        };
        
        class Impl : public Interface
        {
          public:
          virtual void callback() { std::cout << "Hi from Impl\n"; }
        };
        
        class User
        {
          public:
          User(Interface& newCallback) : myCallback(newCallback) { }
        
          void DoSomething() { myCallback.callback(); }
        
          private:
          Interface& myCallback;
        };
        
        int main()
        {
          Impl cb;
          User user(cb);
          user.DoSomething();
        }
        

        【讨论】:

          【解决方案5】:

          有各种库可以让你做到这一点。查看 boost::function。

          或者尝试你自己的简单实现:

          template <typename ClassType, typename Result>
          class Functor
          {
           typedef typename Result (ClassType::*FunctionType)();
           ClassType* obj;
           FunctionType fn;
          public:
           Functor(ClassType& object, FunctionType method): obj(&object), fn(method) {}
          
           Result Invoke()
           {
            return (*obj.*fn)();
           }
          
           Result operator()()
           {
            return Invoke();
           }
          };
          

          用法:

          class A
          {
           int value;
          public:
           A(int v): value(v) {}
          
           int getValue() { return value; }
          };
          
          
          int main()
          {
           A a(2);
          
           Functor<A, int> fn(a, &A::getValue);
          
           cout << fn();
          }
          

          【讨论】:

            【解决方案6】:

            C++ 允许在成员对象上使用函数指针。
            有关详细信息,请参阅here
            您也可以使用boost.signalsboost.signals2(取决于您的程序是否为多线程)。

            【讨论】:

            • 是的,但只有静态成员函数。但我希望通知对象本身
            • 不,任何成员函数都可以是指向成员函数的指针。
            • @Mat 我坚持认为,因为 Mat 似乎没有掌握它:C++ 允许成员非静态函数指针。然而,做你想做的最聪明的方法是仿函数(实际上它只是定义括号运算符,以便你的对象可以像函数一样表现),就像 MeThinks 回答的那样。
            • @Mat 这里是一个小例子:class A{ public: void Foo(){};}; typedef (void) (A::*)() PFuncNonStatic;一个; PFuncNonStatic 函数 = A::Foo; a.(*func)();我可能犯了一些错误,我没有尝试编译,但它或多或少是非静态函数指针的语法精神。
            猜你喜欢
            • 1970-01-01
            • 2023-04-05
            • 2011-06-18
            • 2014-07-10
            • 2016-12-29
            • 1970-01-01
            • 1970-01-01
            • 2015-02-12
            • 1970-01-01
            相关资源
            最近更新 更多