面向对象的C ++回调?

时间:2010-08-19 08:52:46

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

是否有一些库可以让我轻松方便地在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);
    }
}

输出将是: 嗨来自Bar! 3嗨来自Foo!

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

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

任何人都有一些提示给我?

谢谢!

6 个答案:

答案 0 :(得分:11)

在C ++中使用Callbacks的最多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();
}

答案 1 :(得分:5)

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

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

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 :: function中提供的函数包装器,但我还将在此处演示一个简单的包装器。它与第一个概念类似,但隐藏了实现并接受了大量其他解决方案。我个人通常将此作为我选择的回调方法。

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 :(得分:2)

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

答案 3 :(得分:1)

有各种各样的库可以让你这样做。查看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();
}

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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