事件回调守护进程
我正在 C++ 中开发一个事件守护程序,我想使用成员函数回调。基本上,事件队列会收集守护进程持续服务的事件。有一个带有 ID 的基类 Event 结构,所有事件都将从它派生。我希望为每个事件注册的方法在其签名中使用派生事件类型。
struct Event
{
unsigned int eventId;
};
struct EventA : public Event
{
unsigned int x;
unsigned int y;
};
// and struct EventB, EventC (use your imagination...)
const unsigned int EVENT_A = 1;
const unsigned int EVENT_B = 2;
const unsigned int EVENT_C = 3;
class Foo
{
public:
void handlerMethod_A(const EventA& e);
void handlerMethod_B(const EventB& e);
};
class Bar
{
public:
void handlerMethod_C(const EventC& e);
};
然后守护进程将允许这些类使用它们的“this”指针订阅它们的成员函数。
class EventDaemon
{
public:
void serviceEvents();
template <class CallbackClass, class EventType>
void subscribe(
const unsigned int eventId,
CallbackClass* classInstancePtr,
void (CallbackClass::*funcPtr)(EventType));
private:
Queue<Event*> eventQueue_;
};
因此,在此类之外,您可以执行以下操作:
EventDaemon* ed = new EventDaemon();
Foo* foo = new Foo();
Bar* bar = new Bar();
ed->subscribe(EVENT_A, foo, Foo::handlerMethod_A);
ed->subscribe(EVENT_B, foo, Foo::handlerMethod_B);
ed->subscribe(EVENT_C, bar, Bar::handlerMethod_C);
并且 EventDaemon 循环将遵循以下内容
void EventDaemon::serviceEvents()
{
while (true)
{
if (eventQueue_.empty())
{
// yield to other threads
}
else
{
// pop an event out of the FIFO queue
Event e* = eventQueue_.pop();
// somehow look up the callback info and use it
classInstancePtr->*funcPtr(reinterpret_cast<?*>(e));
}
}
}
所以我的问题是如何通过事件 ID 将“this”指针和成员函数指针存储在某种数组中。这样我就可以通过使用 e->eventId 和事件类型以及重新解释转换来查找“classInstancePtr”和“funcPtr”。
I am working on an event daemon in C++ that I would like to use member function callbacks. Basically an event queue would collect events which the daemon continuously services. There is a base class Event struct with an ID and all events would derive from it. I would like the methods registered for each event to use the derived event type in their signature.
struct Event
{
unsigned int eventId;
};
struct EventA : public Event
{
unsigned int x;
unsigned int y;
};
// and struct EventB, EventC (use your imagination...)
const unsigned int EVENT_A = 1;
const unsigned int EVENT_B = 2;
const unsigned int EVENT_C = 3;
class Foo
{
public:
void handlerMethod_A(const EventA& e);
void handlerMethod_B(const EventB& e);
};
class Bar
{
public:
void handlerMethod_C(const EventC& e);
};
Then the Daemon would allow these classes to subscribe their member functions using their 'this' pointer.
class EventDaemon
{
public:
void serviceEvents();
template <class CallbackClass, class EventType>
void subscribe(
const unsigned int eventId,
CallbackClass* classInstancePtr,
void (CallbackClass::*funcPtr)(EventType));
private:
Queue<Event*> eventQueue_;
};
So outside this class you could do something like:
EventDaemon* ed = new EventDaemon();
Foo* foo = new Foo();
Bar* bar = new Bar();
ed->subscribe(EVENT_A, foo, Foo::handlerMethod_A);
ed->subscribe(EVENT_B, foo, Foo::handlerMethod_B);
ed->subscribe(EVENT_C, bar, Bar::handlerMethod_C);
And the EventDaemon loop would be along the lines of
void EventDaemon::serviceEvents()
{
while (true)
{
if (eventQueue_.empty())
{
// yield to other threads
}
else
{
// pop an event out of the FIFO queue
Event e* = eventQueue_.pop();
// somehow look up the callback info and use it
classInstancePtr->*funcPtr(reinterpret_cast<?*>(e));
}
}
}
So my question is how I can store the 'this' pointers and member function pointers in some sort of array by event ID. That way I could look up the 'classInstancePtr' and 'funcPtr' by using e->eventId and the event type as well for the reinterpret cast.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你工作太辛苦了。使用boost函数:
http://www.boost.org/doc /libs/1_47_0/doc/html/function.html
无论您是否有对象,这些都可以工作。它们会增加你的编译时间。
请注意,每当您遇到此类问题时,您知道很多人一定遇到过同样的问题,可能有一个简单的选项,如果它不在标准库中,则可能在 boost 中。
为了回应 Nick,我不断地将 boost 函数对象放入向量之类的东西中。
我发现,虽然 boost 函数对象可以保存对象引用,但让它们这样做可能会导致对象生命周期出现错误,并且最好让它们保存类对象的副本(无论您尝试什么,您都会遇到相同的错误)持有对您不一定控制其生命周期的对象实例的引用)。该模式:
所有成员变量都保存在shared_ptr中,可以实现良好的性能,并且您不必担心函数对象保存的对象的生命周期,因为您可以按值复制它们。线程代码(我现在似乎总是在写)需要额外的东西,例如成员中至少一个增强互斥元素或其他某种方式来确保值不会被踩踏。
You are working too hard. Use boost functions:
http://www.boost.org/doc/libs/1_47_0/doc/html/function.html
These work whether you have a object or not. They will increase your compile time.
Note, whenever you come across these types of questions where you know many people must have had the same problem, there is probably a simple option and, if it is not in the standard library, it is probably in boost.
In response to Nick, I'm constantly throwing boost function objects into vectors and whatnot.
I've found that, while boost function objects can hold object references, having them do so can lead to bugs with object lifetimes and it is better to have them hold copies of the class objects (you run into the same bugs however you try to hold a reference to a object instance that you don't necessarily control the lifetime of). The pattern:
where all the member variables get held in a shared_ptr allows for good performance and you don't have to worry about lifetimes of objects held by function objects because you can copy them by value. Threaded code (what I always seem to be writing nowadays) needs additional things like at least one boost mutex element in Member or some other way to assure values don't get stomped on.
boost::function
[或者,如果您的系统支持它,std::function
] 将很好地保存this
指针,如果不需要的话,还有一个额外的好处,那就是不需要实际的对象。因此,您使用的是std::function
,而不是void (SomeType::*)(EventA)
,并且调用std::根据需要进行绑定。
一个简单的包装函数可用于提供与您最初提议的相同的签名并隐藏令人讨厌的占位符。
当然,您仍然面临每个事件类型都有自己的签名的问题,并且需要确保使用正确的事件 ID 代码。在这两种情况下,您的基本事件类型都可以提供帮助。您的回调不需要接受
EventA&
;它可以接受Event&
,并在运行时将其dynamic_cast
转换为EventA
。对于ID,直接查询类型。现在,如果您有一个
Event*
对象 [当您去调度事件时],您可以执行p->ID()
来获取适当的 ID,并且如果您有一个EventA
类型[当您注册回调时],您可以执行EventA::EventID
。因此,现在,您只需存储一个
std::function
以及每个回调的关联int
值,无论您所举办的活动的实际类型是什么。您仍然遇到这样的问题:订阅时的用户错误可能会导致函数被错误调用。如果您在回调中正确使用了
dynamic_cast
,这将在运行时被捕获,但编译时检查会很好。那么,如果我们自动化dynamic_cast
呢?对于此步骤,我将使用 c++11 lambda,但也可以使用多种方法在 C++03 中实现。因此,现在我们已经回到了原始界面,您的回调接受它们将要处理的实际类型,但在内部您已将它们全部压缩到一个共同的签名中。
boost::function
[or, if your system supports it,std::function
] will take care of holding thethis
pointer quite well, with the added benefit of not requiring an actual object if it isn't necessary. So instead ofvoid (SomeType::*)(EventA)
you havestd::function<void(EventA)>
, and you callstd::bind
as appropriate.A trivial wrapper function can be used to provide the same signature as you originally proposed and hide the nasty placeholders.
You do, of course, still have the issue of each event type having its own signature, and the need to ensure you use the correct Event ID code. In both cases, your base Event type can help out. Your callback need not accept an
EventA&
; it can accept anEvent&
, anddynamic_cast
it to anEventA
at runtime. For the ID, query the type directly.Now, if you have an
Event*
object [when you go to dispatch your events], you can dop->ID()
to get the appropriate ID, and if you have aEventA
type [when you register your callbacks] you can doEventA::EventID
.So now, all you have to store is a
std::function<void(const Event&)>
and an associatedint
value for each of your callbacks, no matter what the actual type of event you have.You still have the issue that user error when subscribing can result in a function being called incorrectly. If you've used
dynamic_cast
correctly within the callback, this will get caught at runtime, but a compile time check would be nice. So what if we automate thatdynamic_cast
? For this step, I'm going to use c++11 lambdas, but it can be implemented in C++03 as well using a variety of methods.So now we've gone full circle back to your original interface where your callbacks accept the actual type they are going to be working on, but internally you've squeezed them all into a common signature.
好的,我完成了我原来想要的接口的实现。我正在查看丹尼斯的答案,但最终找到了函子,我意识到我正在寻找的是一个简单的多态解决方案。在此之前,我无法理解我可以创建一个非模板化基类,用于在向量/数组中存储模板化类。我想这就是 mheyman 想要告诉我的……所以我很抱歉我没有立即明白。只是为了澄清一下,尽管我实际上是为了自己的利益和知识而寻找实施解决方案,而不仅仅是第三方库来完成工作。所以我想我会寻找 Boost 函数如何工作,而不仅仅是它们存在并且很棒。
如果有人仍然感兴趣,这里是我最终得到的重要部分(减去一些无关的东西和错误检查):
EventDaemon 循环
是使用模板化类型进行动态转换的。我的最后步骤将是尝试消除让开发人员为每个事件定义 ID 的需要......当然,这可能会结束稍后会有新帖子 星期。
Okay, so I finished an implementation of my original desired interface. I was looking through Dennis' answer but eventually got lead to functors and I realized what I was looking for was a simple polymorphic solution. I failed to grasp before that I could create a non-templated base class with which to use for storing templated classes in vectors/arrays. I think this is what mheyman was trying to tell me... so I apologize I didn't get it right away. Just to clarify though I was really looking for the implementation solution for my own benefit and knowledge, not just a 3rd party library to get the job done. So I guess I would be looking for how Boost functions work, not just that they exist and are awesome.
If anyone is still interested here are the important parts of what I ended up with (minus some extraneous stuff and error checking):
EventDaemon loop
My final steps here will be to try and remove the need to have the developer define an ID for each event... of course this might end up a new post later this week.