我的事件管理器
对于事件管理器,我需要很多函数指针存储在一个矢量打电话给他们时,该事件被触发。 (我将提供在这个问题的端部的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
会听取事件"PlayerLevelup"
与它的成员函数ComponentSound::PlayerLevelup
如果触发事件打出一场漂亮的声音。
在C ++中的成员函数指针看起来像这样。
// ReturnType (Class::*MemberFunction)(Parameters);
void (ComponentSound::*PlayerLevelup)();
问题是,任何组件类应该能够侦听事件,但存储成员函数指针在事件管理器需要我指定的听力课。 正如你所看到的例子,我需要指定ComponentSound
但事件管理器应该只是有成员函数指针的向量任一类别。
题
这些问题的一个答复会帮助我很多。
- 我怎样才能函数指针存储到任何成员函数在我的事件管理器的载体? (也许它可以帮助所有的监听功能,从一个抽象类继承的
Component
)。 - 我怎样才能设计出我的事件管理器以另一种方式来达到目的的功能? (我想用字符串和int键的消息。)
我试图让我的问题一般,但如果你需要更多的信息或代码,请发表评论。
分配
在我的成员函数指针的矢量I使用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(); }
};
没有一个直接的反应,所以大家多多包涵。
在我们开始之前:这通常被称为Observer
模式,你可能会发现很多关于它在网络上的信息混乱,许多失败的实现,但谁知道你还可能打击黄金。
好了,所以首先这个问题有一个根本的缺陷:它没有考虑到拍摄对象的引用是棘手的 ,因为对象的寿命是有界的。
因此,即使在我们深入研究了实现的细节,我们需要问自己如何处理过期引用 。 有两种基本策略:
没有过时的引用,这意味着注册的对象自动注销自己在破坏。 该机构可以在一个基类被分解。
检查他们的时候,懒洋洋地收集陈旧的人有一种方法来分辨好和陈旧引用。 该机构可以采用强制执行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
你将不得不使用std::function
。 这是实现一个通用的回调的唯一途径。 只要你涉及函数指针,而不是函数对象,它是不通用的,将永远是通用的,且永远不能做成通用的。
unordered_map<string, vector<std::function<void()>>>
函数指针是坏的,不应该在C ++中明确使用,只传递给像模板std::bind
和std::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
这是你应该使用多态,而不是函数(或成员函数)指针的典型案例。
正如你指出,你的组件类必须从通用类组件,其中包含虚拟方法(S)表示事件(一个或多个)继承:
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;
作为可选无效* paramenter事件的方法,你可以指定它作为可选参数,但事实证明这是一个void *是一个不好的征兆(用void *,可导致类型安全的损失),所以我会建议你找一个不同的方式来实现你想要什么。