7

我正在尝试向我的项目添加一个简单的消息传递系统,其中事件可以由函数调用,这将导致注册到该事件的所有回调都被调用。

现在,执行此操作的合乎逻辑的方法是使用函数指针。很容易将指向所需回调函数的指针传递给事件管理器,以进行注册。事件回调函数将始终返回 anint并将 avoid*作为参数。

但是我不想将静态全局函数注册为我的事件回调 - 我想用类成员函数来做。

  • 甚至有可能用 C++ 实现这一点吗?存储和调用指向不同类但具有相同函数头的成员函数的指针。

  • 如果这是不可能的,你对我如何解决这个问题有什么建议吗?我真的很想将事件侦听器直接添加到我的课程中。

4

7 回答 7

8

对的,这是可能的。 C++0x 有function处理这个的类,正如其他人指出的那样,Boost 有类似的设施。

您也可以自己滚动,但语法不适合胆小的人:

#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;
}

这演示了一种以不透明方式处理自由函数和成员函数的方法。这是一个简单的示例,可以通过多种方式使其更加通用和健壮。我会向您推荐这些页面以获取语法帮助:

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

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

我还建议您查看此内容以获取更多想法:

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

于 2011-03-03T13:53:58.980 回答
3

当然有可能!看看Boost.Signal2Boost.Bind

Boost.Signal2 基本上实现了一个信号和插槽系统,这正是您所需要的。然后,您可以使用boost::bindwhich is a generalization of std::bind1standstd::bind2nd将函数对象包装器用于基本上您能想到的任何东西(在您的情况下,成员方法)。它真的很强大。

请参阅此官方提升教程

于 2011-03-03T12:51:35.123 回答
2

这是我做这样的工作的不太好的尝试:首先你需要一个基本的事件处理程序类,现在我们称之为 EvtHandler :

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

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

然后每个应该以某种方式处理事件的类都应该派生自这个类,并且只要它们返回相同的数据类型(在这种情况下为void)并接收相同的参数,它们就可以尽可能多地实现新功能(本例中的事件)。像这样:

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

然后我为每个特殊事件实现了消息中心,它必须在需要时注册侦听器并调度事件:

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;
};

然后,您可以将您的 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 ();

但是再一次,这根本不好,只是想我会分享!抱歉弄乱了,哈哈!

于 2011-03-03T14:04:40.883 回答
1

我不完全确定您要存档什么,但也许您应该查看Boost Signals2

如果您想创建某种 Signal/Slot 机制,这将非常有帮助。

于 2011-03-03T12:54:27.927 回答
0

No, it is not possible (unless you do c++/cli with .net).

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).

于 2011-03-03T12:49:30.527 回答
0

我管理的最接近的方法是将静态成员函数注册为回调。除了事件处理程序发送的参数之外,静态成员还将对象 (this) 指针作为参数,并使用 this 来调用成员函数。

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

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

注册myClass::callbackthis与您的处理程序。如果您在可以返回的内容方面受到限制,您可能需要将其包装在 arg 引用的结构中。

于 2011-03-03T12:54:21.353 回答
0

我在 SWIG 中使用 lukes 答案,因为 SWIG 不支持所有 C++11 功能......这可能可以通过 Parsa Jamshidis 方法进一步改进。

我对其进行了修改以涵盖更多情况(可变数量的参数和可变返回类型):

#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...
}
于 2019-01-03T15:22:55.197 回答