将功能指针存储到任何成员功能

Store Function Pointers to any Member Function

本文关键字:功能 任何 成员 存储 指针      更新时间:2023-10-16

我的事件经理

对于事件管理器,我需要将许多指针存储在矢量中的功能以在事件触发时调用它们。(我将在此问题结束时提供EventFunction Helper类的源代码。)

// 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之类的组件将听取事件"PlayerLevelup",并使用其成员函数ComponentSound::PlayerLevelup播放,如果触发了该事件,则可以播放不错的声音。

C 中的成员函数指针看起来像这样。

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

问题是,任何组件类都应该能够收听事件,但是将成员函数指针存储在事件管理器中需要我指定听力类。如您在示例中所看到的,我需要指定ComponentSound,但是事件管理器应仅将成员功能指针的向量放在任何类中。

问题

对这些问题之一的答案将对我有很大帮助。

  • 我如何将功能指针存储在我的活动经理中的向量中的任何成员函数中?(也许有助于所有聆听功能均从一个抽象类Component继承。)
  • 如何以另一种方式设计我的活动经理以达到目标功能?(我想将字符串和int键用于消息。)

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

作业

在我的成员功能指针的向量中,我使用事件功能,而不仅仅是提供两种消息类型的指针。一个与数据参数一起。

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

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

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

功能指针很差,绝不应在C 中明确使用,仅传递给std::bindstd::function的构造函数等模板,并且成员函数指针更糟。

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-and-functors

不是直接的回应,请忍受我。

在开始之前:通常将其称为Observer模式,您可能会在网络上找到很多关于它的困惑信息,以及许多失败的实现,但是谁知道您也可能会撞到黄金。

好吧,首先,问题具有一个基本缺陷:它未能考虑捕获对象引用很棘手,因为对象寿命是有界的。

因此,甚至在我们深入研究实施的细节之前,我们都需要问自己如何处理陈旧的参考。有两种基本策略:

  1. 没有过时的参考,这意味着注册对象 unregister 在破坏时会自动自动。该机制可以分解在基类中。

  2. 在检查它们时,有一种方法可以说出良好和陈旧的参考,并懒洋洋地收集陈旧的参考。可以使用shared_ptr/weak_ptr对执行该机制,并意识到weak_ptr shared_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

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

正如您所指出的,您的组件类应从公共类组件继承,该类别包含代表事件的虚拟方法:

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* paramenter,您可以将其指定为可选参数,但是它是一个void*是一个不好的符号(使用void*可以导致类型安全损失),因此我建议您寻找另一种实现自己想要的方式。