简体   繁体   English

C ++:类成员用作事件回调

[英]C++: Class member functions as event callbacks

I'm trying to add a simple messaging system to my project, where events can be invoked by a function, which will lead to all callbacks registered to that event being called. 我正在尝试向我的项目添加一个简单的消息传递系统,其中事件可以由函数调用,这将导致注册到该事件的所有回调被调用。

Now, the logical way to do this is using function pointers. 现在,执行此操作的逻辑方法是使用函数指针。 It would be easily possible to pass the pointer to the desired callback function to the events manager, for registering. 可以很容易地将指针传递给事件管理器所需的回调函数,以进行注册。 An event callback function would always return an int and take a void* as argument. 事件回调函数总是返回一个int并将void*作为参数。

However I don't want to register static global functions as my event callbacks - I'd like to do it with class member functions . 但是我不想将静态全局函数注册为我的事件回调 - 我想用类成员函数来做。

  • Is it even possible to accomplish this with C++? 是否有可能用C ++实现这一目标? Storing and calling pointers to member functions of different classes but with the same function header . 存储和调用指向不同类的成员函数但具有相同函数头的指针。

  • If this is not possible, do you have any suggestions on how I could work around this? 如果这是不可能的,你对我如何解决这个问题有什么建议吗? I'd really like to add event listeners directly to my classes. 我真的想直接向我的类添加事件监听器。

Yes it is possible. 对的,这是可能的。 C++0x has the function class that handles this , and as others have pointed out Boost has similar facilities. C ++ 0x具有处理此问题的function ,并且正如其他人指出的那样,Boost具有类似的功能。

You can also roll your own, but the syntax is not for the faint of heart: 你也可以自己动手,但语法不适合胆小的人:

#include <iostream>

class Callable
{
    public:

        virtual ~Callable() {}
        virtual int operator() (void* args) = 0;
};

class CallableFreeFunction  : public Callable
{
    public:

        CallableFreeFunction(int (*func)(void*)) : func_(func) {}

        virtual int operator() (void* args) { return (*func_)(args); }

    private:

        int (*func_)(void*);
};

template <typename tClass>
class ClassMemberCallable : public Callable
{
    public:

        ClassMemberCallable(tClass* instance, int (tClass::*memberfunction)(void*)) : instance_(instance), memberfunc_(memberfunction) {}

        virtual int operator() (void* args) { return (instance_->*memberfunc_)(args); }

    private:

        tClass* instance_;
        int (tClass::*memberfunc_)(void*);
};

class Foo
{
    public:

        int derp(void* args)
        {
            std::cout << args << '\n';
            return 2;
        }
};

int freefunctionfoo(void* args)
{
    std::cout << "free" << args << '\n';
    return 2;
}

int main(int argc, char* argv[])
{
    Foo myfoo;

    Callable* callable = new ClassMemberCallable<Foo>(&myfoo, &Foo::derp);

    (*callable)(0);

    delete callable;

    callable = new CallableFreeFunction(freefunctionfoo);

    (*callable)(0);

    delete callable;

    std::cin.get();

    return 0;
}

This demonstrates a way of handling both free functions, and member functions in an opaque way. 这演示了一种以不透明的方式处理自由函数和成员函数的方法。 This is a simple example, and can be made more generic and robust in a number of ways. 这是一个简单的例子,可以通过多种方式使其更通用和更健壮。 I'd refer you to these pages for syntax help: 我会向您推荐这些页面以获取语法帮助:

http://www.newty.de/fpt/index.html http://www.newty.de/fpt/index.html

http://www.parashift.com/c++-faq-lite/pointers-to-members.html http://www.parashift.com/c++-faq-lite/pointers-to-members.html

I'd also recommend looking at this for more ideas: 我还建议您查看更多想法:

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

Of course it's possible ! 当然有可能! Have a look at Boost.Signal2 and Boost.Bind . 看看Boost.Signal2Boost.Bind

Boost.Signal2 basically implements a signal and slots system which is exactly what you need. Boost.Signal2基本上实现了信号和插槽系统,这正是您所需要的。 Then, you can use boost::bind which is a generalization of std::bind1st and std::bind2nd to get function object wrappers to basically anything you can think of (in your case, member methods). 然后,您可以使用boost::bind这是std::bind1ststd::bind2nd的泛化,以获取函数对象包装器,基本上是您能想到的任何东西(在您的情况下,成员方法)。 It's really powerful. 它真的很强大。

See this official boost tutorial . 请参阅此官方增强教程

Here is my not-so-good attempt for doing a job like that: First of all you need a base event handler class, well let's call it EvtHandler for now: 这是我做这样的工作的不太好的尝试:首先你需要一个基本事件处理程序类,现在让我们称之为EvtHandler:

class Event; //implement this yourself, it shall contain general but good info about event

class EvtHandler
{
public:
    virtual void handleEvent (Event & evt);
};

Then every class that is supposed to handle events in a way, should derive from this class, and they can implement new functions as much as they want as far as they return the same data type ( void in this case) and recieve the same paramteres ( Event in this case). 然后,每个应该以某种方式处理事件的类应该派生自这个类,并且只要它们返回相同的数据类型(在这种情况下为void ),它们就可以实现新功能,并接收相同的参数。 (在这种情况下的事件 )。 Like this: 像这样:

class Foo : public EvtHandler
{
public:
    void handleFooEvent (Event & event);
};

Then I implemented message centers for each special event, which had to register listeners and dispatch events when needed: 然后我为每个特殊事件实现了消息中心,必须在需要时注册监听器和调度事件:

class ShutdownMessageCenter
{
typedef std::map<EventHandler *, event_func> ListenerMap; 
public:

    void register (EvtHandler * handler, void(EvtHandler::*memFunc)(Event &)) {
        m_lmap[handler] = memFunc;
    }

     void callListeners () {
         Event shutdown_event (EM_SHUTDOWN /*just imagine this can mean something, idk*/);
         ListenerMap::iterator itr = m_lmap.begin ();
         for (; itr != m_lmap.end(); ++itr) {
            EvtHandler * handler = itr->first;
            void (EvtHandler::*func)(Event &) = itr->second;
            (handler->*func)(shutdown_event);
          }
      }

private:
   ListenerMap m_lmap;
};

Then you could register your EvtHandlers to this particular message center for example! 然后,您可以将EvtHandlers注册到此特定消息中心,例如!

ShutdownMessageCenter message_center;
EvtHandler * some_handler = new EvtHandler ();
Foo * some_foo = new Foo ();
message_center.register (some_handler, &EvtHandler::handleEvent);
message_center.register (some_foo, static_cast<void (EvtHandler::*)(Event &)>(&Foo::handleFooEvent);
message_center.callListeners ();

But once again this is not good at all, just thought I would share! 但是再一次这根本不好,只是想我会分享! Sorry for the mess, haha! 抱歉这个烂摊子,哈哈!

I am not completely sure what you want to archive but maybe you should look at Boost Signals2 我不完全确定你要归档什么,但也许你应该看看Boost Signals2

It is quite helpful if you want to create some sort of Signal/Slot mechanism. 如果你想创建某种信号/插槽机制,这是非常有用的。

No, it is not possible (unless you do c++/cli with .net). 不,这是不可能的(除非您使用.net执行c ++ / cli)。

Now, you can still create static functions, pass them an object as a parameter, and the only thing that they'll do is call your member function on that object. 现在,您仍然可以创建静态函数,将对象作为参数传递给他们,他们唯一要做的就是在该对象上调用您的成员函数。 (Actually a cast will be required first). (实际上首先需要演员表)。

The closest that I have managed is to register a static member function as the callback. 我管理的最接近的是注册一个静态成员函数作为回调。 The static member takes the object (this) pointer as an argument in addition to the arguments sent by the event handler and uses this to call the member function. 除了事件处理程序发送的参数之外,静态成员还将object(this)指针作为参数,并使用它来调用成员函数。

class myClass{
public:
  static void callback(void *arg, void *obj)
  {
    if (obj)
      reinterpret_cast<myClass*>(obj)->cb(arg);
  }

private:
  void cb(void *arg);
};

Register myClass::callback and this with your handler. 注册myClass::callbackthis与您的处理程序。 You may need to wrap this in the structure that arg references if you are restricted in what can be returned. 如果您可以返回的内容受到限制,则可能需要将此包装在arg引用的结构中。

I am using lukes answer with SWIG because SWIG does not support all C++11 features... This probably can be improved even further with Parsa Jamshidis approach. 我正在使用SWIG回答SWIG,因为SWIG不支持所有C ++ 11功能......这可能会通过Parsa Jamshidis方法进一步改进。

I modified it to cover even more cases (variable amount of arguments and variable return type): 我修改它以涵盖更多的情况(可变数量的参数和变量返回类型):

#include <iostream>

template <typename R, typename ...T>
class Callback
{
public:
    virtual ~Callback() {}
    virtual R operator() (T... args) = 0;
};

template <typename R, typename ...T>
class FreeCallback : public Callback<R, T...>
{
public:
    FreeCallback(R(*func)(T...)) : func_(func) {}
    virtual R operator() (T... args) { return (*func_)(args...); }
private:
    R(*func_)(T...);
};

template <typename tClass, typename R, typename ...T>
class MemberCallback : public Callback<R, T...>
{
public:
    MemberCallback(tClass* instance, R (tClass::*memberfunction)(T...)) : instance_(instance), memberfunc_(memberfunction) {}
    virtual R operator() (T... args) { return (instance_->*memberfunc_)(args...); }
private:
    tClass * instance_;
    R (tClass::*memberfunc_)(T...);
};

class foo {
  public:
  Callback<int, int> *IntCallback;
  Callback<int, int, double, double> *IntDoubleDoubleCallback;
};

class blub {
  public:
  int func1(int i) {
    std::cout << "args: " << i << std::endl;
    return 1;
  }
  int func2(int i, double d1, double d2){
    std::cout << "args: " << i << " " << d1 << " " << d2 << std::endl;
    return 0;
  }
};

  int freeFunc1(int i) {
    std::cout << "args: " << i << std::endl;
    return 1;
  }
  int freeFunc2(int i, double d1, double d2){
    std::cout << "args: " << i << " " << d1 << " " << d2 << std::endl;
    return 0;
  }

int main() {
  foo f;
  blub b;
  f.IntCallback = new MemberCallback<blub, int, int>(&b, &blub::func1);
  f.IntDoubleDoubleCallback = new MemberCallback<blub, int, int, double, double>(&b, &blub::func2);

  Callback<int, int> *IntFreeCallback = new FreeCallback<int, int>(&freeFunc1);
  Callback<int, int, double, double> *IntDoubleDoubleFreeCallback = new FreeCallback<int, int, double, double>(&freeFunc2);

  int ret = (*IntFreeCallback)(42);
  std::cout << "ret freeFunc1: " << ret << std::endl;
  ret = (*IntDoubleDoubleFreeCallback)(42, 3.1415, 2.7182);
  std::cout << "ret freeFunc2: " << ret << std::endl;


  ret = (*f.IntCallback)(42);
  std::cout << "ret func1: " << ret << std::endl;
  ret = (*f.IntDoubleDoubleCallback)(42, 3.1415, 2.7182);
  std::cout << "ret func2: " << ret << std::endl;
  std::cout << "Hello World!\n";
  // cleanup not done here...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM