【问题标题】:Good way to add event handlers?添加事件处理程序的好方法?
【发布时间】:2010-10-10 04:25:56
【问题描述】:

我正在为游戏制作一个 Gui API。基本上我的类中有事件回调,它们是函数指针。我想到了直接让用户=函数指针ex:

widget->OnPaintCallback = myPaintFunc;

但我不喜欢我无法检查 NULL 或做任何其他事情的方式。这也让我的班级感到暴露。

我也想过为每个回调设置一个设置器,但这会在课堂上变得混乱(我有超过 50 个)

然后我想到了一个函数,它请求一个字符串,指示处理程序是针对哪个事件,以及它的函数指针。但这会演变为不必要地参考文档来了解字符串,并且对于自定义未记录的小部件来说更加混乱。

有没有更好、更清洁的替代品?

谢谢

casablankca 的解决方案可以有多个参数吗?

【问题讨论】:

    标签: c++ event-handling


    【解决方案1】:

    我建议使用Boost.Signals 库。像这样的:

    class Widget
    {
    public:
        boost::signal<void (Paint &)> onPaint;
        boost::signal<void (MouseMove &)> onMouseMove;
        // ... etc
    };
    
    // later...
    
    Widget myWidget;
    myWidget.onPaint.connect(myPaintFunc);
    
    // and to fire the event:
    
    void Widget::DoPaint()
    {
        Paint data;
        data.whatever = foo;
    
        onPaint(data);
    }
    

    这有几个优点:

    1. 您可以将它与boost::bind(或bind 的C++0x 版本,如果您的编译器支持)结合使用,以便将成员函数绑定到事件处理程序。
    2. 您可以将多个处理程序附加到单个事件。如果只使用函数指针,那么一次只能分配一个函数指针。
    3. 信号类型强且灵活。您可以拥有采用不同类型和数量的参数的信号,它们都将在编译时解析。

    【讨论】:

    • 有非 boost 替代品吗?
    【解决方案2】:

    我建议查看boost.signals 库或libsigc++。这些是用于管理事件处理程序之类的非常通用的库。它们比您尝试做的要多得多,但它们会为您提供您尚未想到的设计想法。

    您使用回调的次数越多,您就越会意识到您需要这些库中的更多功能(例如注册多个回调、绑定参数、更灵活的类型等)所以即使您最终这样做了一些更简单的东西,从成熟的设计中学习会很有帮助。

    【讨论】:

      【解决方案3】:

      但我不喜欢我无法检查 NULL 或做任何其他事情的方式

      如何使回调 (OnPaintCallback) 成为重载 operator = 的类的对象,这样您就可以进行任何额外的检查并在出现问题时抛出异常。你也可以重载operator (),这样你就可以像调用一个简单的函数指针一样调用这个对象。

      更新:至于可变数量的函数参数,没有通用的方法,但如果你的最大 N 是有限且小的,你可以使用模板特化,例如:(I '为了清楚起见,省略了构造函数、operator = 和其他细节)

      template<typename T, int N>
      class Callback {
      };
      
      template<typename T>
      class Callback<T, 1> {
        T func;
      
        template<typename A1>
        void operator ()(A1 arg1) {
          func(arg1);
        }
      };
      
      template<typename T>
      class Callback<T, 2> {
        T func;
      
        template<typename A1, typename A2>
        void operator ()(A1 arg1, A2 arg2) {
          func(arg1, arg2);
        }
      };
      

      我知道这是一种很老套的方法,但至少您的用户不会看到任何这些:他们将获得所有回调的相同界面。

      【讨论】:

      • 我怎样才能有 N 个参数?
      • @Milo:你的意思是不同的回调可以有不同数量的参数?
      • 是的,绘画可能不需要任何东西,但点击可能需要位置
      • 谢谢,我只使用模板和 1 个参数
      【解决方案4】:

      您可以为所需的每种类型的事件处理程序创建一个接口。与 Java 不同。所以例如你会有

      class PaintCallback {
      public:
        virtual void paint() = 0;
      };

      您的事件处理程序将从抽象类继承并实现绘制方法。在小部件类中,您将为每个处理程序保留一个指针(或集合)。

      【讨论】:

      • 不,我真的想要事件处理程序,我不认为类似插件的设计对此有好处,我想要更像 .Net
      猜你喜欢
      • 1970-01-01
      • 2020-04-24
      • 2012-01-18
      • 1970-01-01
      • 1970-01-01
      • 2013-12-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多