【问题标题】:Store Function Pointers to any Member Function存储指向任何成员函数的函数指针
【发布时间】:2012-10-20 12:14:40
【问题描述】:

我的活动管理器

对于事件管理器,我需要将许多指向函数的指针存储在向量中,以便在触发事件时调用它们。 (我会在本题末尾提供EventFunction辅助类的源码。)

// an event is defined by a string name and a number
typedef pair<string, int> EventKey;

// EventFunction holds a pointer to a listener function with or without data parameter
typedef unordered_map<EventKey, vector<EventFunction>> ListEvent;

// stores all events and their listeners
ListEvent List;

注册一个监听器可以通过调用第一个或第二个函数来完成,这取决于你是否想要接收额外的数据。 (此代码来自我的事件管理器类。)

public:
  typedef void (*EventFunctionPointer)();
  typedef void (*EventFunctionPointerData)(void* Data);

  // let components register for events by functions with or without data parameter,
  // internally simple create a EventFunction object and call the private function
  void ManagerEvent::Listen(EventFunctionPointer Function, string Name, int State);
  void ManagerEvent::Listen(EventFunctionPointerData Function, string Name, int State);

private:
  void ManagerEvent::Listen(EventFunction Function, string Name, int State)
  {
    EventKey Key(Name, State);
    List[Key].push_back(Function);
  }  

成员函数指针

该代码不起作用,因为我在我的列表中存储了函数指针而不是成员函数指针。所有这些指针都应该是成员函数指针,因为像ComponentSound 这样的组件将通过其成员函数ComponentSound::PlayerLevelup 监听事件"PlayerLevelup",以便在事件被触发时播放美妙的声音。

C++ 中的成员函数指针如下所示。

// ReturnType (Class::*MemberFunction)(Parameters);
void (ComponentSound::*PlayerLevelup)();

问题是,任何组件类都应该能够监听事件,但是在事件管理器中存储成员函数指针需要我指定监听类。正如您在示例中看到的那样,我需要指定 ComponentSound,但事件管理器应该只包含指向任何类的成员函数指针向量。

问题

回答其中一个问题会对我有很大帮助。

  • 如何在事件管理器的向量中存储指向任何成员函数的函数指针? (也许所有监听函数都继承自一个抽象类Component 会有所帮助。)
  • 如何以其他方式设计我的事件管理器以实现目标功能? (我想对消息使用字符串和整数键。)

我试图让我的问题保持一般性,但如果您需要更多信息或代码,请发表评论。

作业

在我的成员函数指针向量中,我使用 EventFunction 而不是仅使用指针来提供两种消息类型。一个有数据参数,一个没有数据参数。

class EventFunction
{
private: EventFunctionPointer Pointer; EventFunctionPointerData PointerData; bool Data;
public:
    EventFunction(EventFunctionPointer Pointer) : Pointer(Pointer), PointerData(NULL), Data(false) { }
    EventFunction(EventFunctionPointerData PointerData) : PointerData(PointerData), Pointer(NULL), Data(true) { }
    EventFunctionPointer GetFunction() { return Pointer; }
    EventFunctionPointerData GetFunctionData() { return PointerData; } bool IsData() { return Data; }
    void Call(void* Data = NULL){ if(this->Data) PointerData(Data); else Pointer(); }
};

【问题讨论】:

    标签: c++ function events function-pointers


    【解决方案1】:

    您必须使用std::function。这是实现通用回调的唯一方法。一旦你涉及到函数指针而不是函数对象,它就不是通用的,永远不会是通用的,也永远不能成为通用的。

    unordered_map<string, vector<std::function<void()>>>
    

    函数指针很糟糕,永远不应该在 C++ 中显式使用,只能传递给 std::bindstd::function 的构造函数之类的模板,而成员函数指针则更糟糕。

    【讨论】:

    • 我以前没有听说过,这听起来很有希望。我认为std::function 和 lambda 函数正是我所需要的!非常感谢!
    • @sharethis:请注意,成员函数的一个问题是它们需要一个实例才能使用,这意味着捕获对对象的引用并保证在您调用时该对象将处于活动状态回来了。这可能会变得很棘手。
    • @MatthieuM。不必要。事实上,没有对象的版本是开箱即用的,例如std::function&lt;R(Foo&amp;, A0, A1)&gt; f = &amp;Foo::bar;。隐式参数是显式的。绑定一个对象是额外的工作,这是真的,也是额外的决定。
    • @LucDanton:是的,我显然只是在谈论这个回调用例。
    • @sharethis: std::function 可以很好地处理非静态函数。问题一定在于你如何绑定它们。
    【解决方案2】:

    您可以使用函子来实现这一点。如果你在你的成员函数周围包裹一个仿函数,你可以用仿函数制作一个向量。仿函数如下所示:

    template <class T> class MyFunctor
    {
    private:
        T* ObjectPtr;
        void (T::*MemberFunction) ();
    public:
        void operator () ()
        {
            return (*this->ObjectPtr.*this->MemberFunction)();
        }
    };
    

    所以基本上一个仿函数会覆盖 () 运算符并返回存储在仿函数类中的成员函数。如果您希望函子使用不同的签名,函子可能会非常复杂,但在本文中您可以获得更多信息。

    http://www.codeproject.com/Articles/7112/Pointers-to-Member-Functions-and-Functors

    【讨论】:

    • 监听函数可以访问其组件类的成员了吗?
    【解决方案3】:

    不是直接回应,所以请耐心等待。

    在我们开始之前:这通常被称为Observer 模式,您可能会在网络上找到很多关于它的混淆信息,以及许多失败的实现,但谁知道您也可能获得成功。

    好的,首先这个问题有一个根本缺陷:它没有考虑到捕获对象引用很棘手,因为对象的生命周期是有限的。

    因此,即使在深入研究实现的细节之前,我们也需要问自己如何处理过时的引用。有两种基本策略:

    1. 没有过时的引用,这意味着已注册的对象在销毁时会自动注销。该机制可以在基类中分解出来。

    2. 在检查它们时有办法区分好的和陈旧的引用,并懒惰地收集陈旧的引用。可以使用shared_ptr/weak_ptr 对来强制执行该机制,并意识到weak_ptrshared_ptr观察者

    两种解决方案都是可行的,而实施都不是完美的。基类机制假设您实际上可以修改您的类层次结构,而 weak_ptr 技巧假设所有观察都将是堆分配的,并且它们的生命周期由 weak_ptr 控制。

    我将使用shared_ptr 做一个示例(并使用许多 C++11 工具,虽然这里没有一个是强制性的):

    class EventManager {
        typedef std::unique_ptr<Observer> OPtr;
        typedef std::vector<OPtr> Observers;
    public:
    
        // Callback observers of "name"
        // Returns the number of observers so invoked
        size_t signal(std::string const& name) const {
            auto const it = _observers.find(name);
            if (it == _observers.end()) { return 0; }
    
            Observers& obs = it->second;
    
            size_t count = 0;
            auto invoker = [&count](OPtr const& p) -> bool {
                bool const invoked = p->invoke();
                count += invoked;
                return not invoked; // if not invoked, remove it!
            };
    
            obs.erase(std::remove_if(obs.begin(), obs.end(), invoker), obs.end());
    
            if (obs.empty()) { _observers.erase(it); }
    
            return count;
        }
    
        // Registers a function callback on event "name"
        void register(std::string const& name, void (*f)()) {
            _observers[name].push_back(OPtr(new ObserverFunc(f)));
        }
    
        // Registers an object callback on event "name"
        template <typename T>
        void register(std::string const& name, std::shared_ptr<T> const& p, void (T::*f)()) {
            _observers[name].push_back(OPtr(new ObserverMember<T>(p, f)));
        }
    
    
    private:
        struct Observer { virtual ~Observer() {} virtual bool invoke() = 0; };
    
        struct ObserverFunc: Observer {
            ObserverFunc(void (*f)()): _f(f) {}
    
            virtual bool invoke() override { _f(); return true; }
    
            void (*_f)();
        };
    
        template <typename T>
        struct ObserverMember: Observer {
            ObserverT(std::weak_ptr<T> p, void (T::*f)()): _p(p), _f(f) {}
    
            virtual bool invoke() override {
                std::shared_ptr<T> p = _p.lock();
                if (not p) { return false; }
    
                p->*_f();
                return true;
            }
    
            std::weak_ptr<T> _p;
            void (T::*_f)();
        };
    
        // mutable because we remove observers lazily
        mutable std::unordered_map<std::string, Observers> _observers;
    }; // class EventManager
    

    【讨论】:

      【解决方案4】:

      这是您应该使用多态性而不是函数(或成员函数)指针的典型情况。

      如您所述,您的组件类应该继承自一个公共类 Component,该类包含表示事件的虚拟方法:

      class Component
      {
      public:
          virtual void OnPlayerLevelUp()
          {
          }
      };
      
      class ComponentSound : public Component
      {
      public:
           // override it
          void OnPlayerLevelUp()
          {
              // do the actual work
          }
      };
      

      您的 ListEvent 类型现在将如下所示:

      typedef unordered_map<EventKey, vector<Component*>> ListEvent;
      

      至于事件方法中可选的 void* 参数,你可以将其指定为可选参数,但它是一个 void* 的事实是一个不好的迹象(使用 void* 会导致类型安全的损失),所以我建议你寻找一种不同的方式来实现你想要的。

      【讨论】:

      • 1+ 的好主意。但这不是我的选择,因为我必须在抽象组件类中声明所有可能的事件。我不想专门声明事件,这是我决定使用字符串并让事件管理器处理事件是否存在的原因。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多