事件回调守护进程

时间:2011-09-09 18:18:07

标签: c++ events templates daemon member-function-pointers

我正在使用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);
};

然后守护进程允许这些类使用他们的'这个'来订阅他们的成员函数。指针。

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

所以我的问题是如何存储这个&#39;这个&#39;由事件ID在某种数组中的指针和成员函数指针。这样我就可以查看“类实际”课程。和&#39; funcPtr&#39;通过使用e-&gt; eventId和事件类型以及重新解释演员。

3 个答案:

答案 0 :(得分:3)

你工作太辛苦了。使用增强功能:

http://www.boost.org/doc/libs/1_47_0/doc/html/function.html

无论您是否拥有某个对象,这些都有效。它们会增加编译时间。

请注意,每当遇到这些类型的问题,你知道许多人必须遇到同样的问题时,可能有一个简单的选项,如果它不在标准库中,它可能会有所提升。

为了回应尼克,我经常将助推功能对象投射到矢量等等中。

我发现,虽然boost函数对象可以保存对象引用,但是让它们这样做可能会导致对象生命周期的错误,最好让它们保存类对象的副本(但是你遇到了相同的bug)您尝试保存对您不一定控制其生命周期的对象实例的引用。模式:

class Foo
{
  struct Member
  {
     // member variable definitions
  };
  shared_ptr<Member> m_; // the only real member variable
public:
  // etc. including the all-important copy
  // constructor and assignment operator and
  // don't forget the member function that gets stuck into
  // the boost function as a callback!
};

其中所有成员变量都保存在shared_ptr中可以获得良好的性能,并且您不必担心函数对象持有的对象的生命周期,因为您可以按值复制它们。线程代码(我现在似乎总是在编写)需要额外的东西,例如在Member中至少有一个boost mutex元素或其他一些方法来确保值不会被踩到。

答案 1 :(得分:1)

boost::function [或者,如果您的系统支持它,std::function]将会很好地保持this指针,并且如果它不需要实际对象的额外好处没有必要。因此,void (SomeType::*)(EventA)取代std::function<void(EventA)>而不是std::bind,而是根据需要调用subscribe(EVENT_A, std::bind(&foo::handleEventA, &foo, std::placeholders::_1));

EventA&

一个简单的包装函数可用于提供与您最初提出的相同的签名,并隐藏讨厌的占位符。

当然,您仍然会遇到每种事件类型都有自己签名的问题,并且需要确保使用正确的事件ID代码。在这两种情况下,您的基本事件类型都可以提供帮助。你的回调不需要接受Event&;它可以在运行时接受dynamic_castEventAstruct Event { virtual void ~Event() { } virtual int ID() =0; }; template<typename E> struct EventHelper : Event { virtual int ID() { return E::EventID; } }; struct EventA : EventHelper<EventA> { static const int EventID = 89; }; 。对于ID,直接查询类型。

Event*

现在,如果你有一个p->ID()对象[当你去发送你的活动时],你可以EventA获取相应的ID,如果你有EventA::EventID类型[当您注册回调时],您可以std::function<void(const Event&)>

现在,无论您的实际活动类型是什么,您需要存储的所有内容都是int和每个回调的关联void subscribe(int id, std::function<void(const Event&)> f) { callbacks.insert(std::make_pair(id, f)); } template<typename E> void subscribe(std::function<void(const Event&)> f) { subscribe(E::EventID, f); } template<typename O, typename E> void subscribe(O* p, void (O::*f)(const Event&)) { subscribe<E>(std::bind(f, p, std::placeholders::_1)); } 值。

dynamic_cast

您仍然遇到订阅时用户错误可能导致错误调用函数的问题。如果你在回调中正确使用了dynamic_cast,这将在运行时被捕获,但编译时检查会很好。那么如果我们自动化template <class CallbackClass, class EventType> void subscribe(CallbackClass* classInstancePtr, void (CallbackClass::*funcPtr)(EventType)) { subscribe<EventType::EventID>([&](const Event& e) { (classInstancePtr->*funcPtr)(dynamic_cast<const EventType&>(e)); }); } 呢?对于这一步,我将使用c ++ 11 lambdas,但它也可以使用各种方法在C ++ 03中实现。

{{1}}

所以现在我们已经完全回到原来的界面,你的回调接受他们将要处理的实际类型,但在内部你已经将它们全部压缩成一个共同的签名。

答案 2 :(得分:0)

好的,所以我完成了原始所需界面的实现。我正在看丹尼斯&#39;回答但最终导致了仿函数,我意识到我在寻找的是一个简单的多态解决方案。之前我没有掌握,我可以创建一个非模板化的基类,用于在矢量/数组中存储模板化类。我想这就是mheyman试图告诉我的......所以我道歉我没有马上得到它。只是为了澄清,虽然我真的在为自己的利益和知识寻找实施解决方案,而不仅仅是第三方图书馆来完成工作。所以我想我会寻找如何 Boost功能,而不仅仅是它们存在且非常棒。

如果有人仍然感兴趣,这里是我最终的重要部分(减去一些无关紧要的东西和错误检查):

  • EventFunctor基本上是指向成员函数模板类
  • 的指针
  • EventFunctorBase是用于将它们存储在向量中的非模板化基类
  • 在用于调用回调之前,使用模板化类型对事件进行动态转换

class EventDaemon
{
public:

    template <class CallbackClass, class EventType>
    void subscribe(
        const EventId eventId,
        CallbackClass* callbackClassInstancePtr,
        void (CallbackClass::*funcPtr)(const EventType&));

private:
    EventFunctorBase* callbacks_[MAX_NUM_EVENTS];
};

template <class CallbackClass, class EventType>
void EventDaemon::subscribe(
    const EventId eventId,
    CallbackClass* callbackClassInstancePtr,
    void (CallbackClass::*funcPtr)(const EventType&))
{
    callbacks_[eventId] = new EventFunctor<CallbackClass,EventType>(callbackClassInstancePtr,funcPtr);
}

class EventFunctorBase
{
public:
    EventFunctorBase();
    virtual ~EventFunctorBase();
    virtual void operator()(const Event& e)=0;
};

template <class CallbackClass, class EventType>
class EventFunctor : public EventFunctorBase
{
public:

    EventFunctor(
        CallbackClass* callbackClassInstancePtr,
        void (CallbackClass::*funcPtr)(const EventType&));

    virtual void operator()(const Event& e);

private:
    CallbackClass* callbackClassInstancePtr_;
    void (CallbackClass::*funcPtr_)(const EventType&);
};

template <class CallbackClass, class EventType>
EventFunctor<CallbackClass,EventType>::EventFunctor(
    CallbackClass* callbackClassInstancePtr,
    void (CallbackClass::*funcPtr)(const EventType&))
    :
    callbackClassInstancePtr_(callbackClassInstancePtr),
    funcPtr_(funcPtr)
{
}

template <class CallbackClass, class EventType>
/*virtual*/ void EventFunctor<CallbackClass,EventType>::operator()(const Event& e)
{
    (callbackClassInstancePtr_->*funcPtr_)(dynamic_cast<const EventType&>(e));
}

EventDaemon循环

while (true_)
{
    if (eventQueue_->empty())
    {
        // yield to other threads
    }
    else
    {
        Event* e = eventQueue_.pop();
        (*(callbacks_[e->ID]))(*e);
    }
}

我在这里的最后一步是尝试删除开发人员为每个事件定义ID的必要性......当然,这可能会在本周晚些时候发布一个新帖子。