3

我有一个带有状态机的类,并且希望有一个单一的入口点来将事件传递给状态机。该事件伴随着特定于事件的数据,然后我想将其分派给处理程序。所以它看起来像这样......

class X
{
  public:
    ...
    template<typename... A> void fsm(eEvent eventId, const A&... eventData);

  private:
    ...
    void eventA(int a, double b);
    void eventB(std::string a);
    void eventC(unsigned long a);

};

...使用看起来像这样的调用...

X x;

x.fsm(eEventA, -1, 2.0);
x.fsm(eEventB, "xyz");
x.fsm(eEventC, 42);

我无法弄清楚如何让模板函数调用正确的处理程序。如果我只是打开 eventId 并传递变量参数,它将无法编译,因为所有参数组合都不存在处理程序(例如,没有 eventA() 处理程序接受 eventB() 参数,我不会无论如何都想要)。

我的猜测是有一些优雅的方法可以做到这一点,但它让我望而却步。

4

4 回答 4

5

选项 1. 抛弃可变参数

如果您没有 C++11 模板参数包,自然的选择是为每个事件声明一个类型,从基本事件类型继承,然后将其打包eventData。这就是我推荐的。您可以使用动态类型来验证是否发送了正确的内容,如果这会减慢速度,请禁用生产模式下的检查。

选项 2. PTMF

使用指向成员函数的指针向调度程序标识事件。这消除了枚举的需要,并将调度的类型直接编码到模板化调度程序中。

但是,这确实要求分派的函数是public.

template<typename... P, typename... A> void fsm(void (X::*event)( P ... ),
    const A&... eventData) {
    this->*event( eventData ... );
}

x.fsm( &X::eventB, 5, 1.3 );
于 2013-01-15T16:52:32.147 回答
0

如果 eEvent 是类似整数的并且在编译时总是已知的,那么您可以对其进行模板化。然后你的调用看起来像

x.fsm<eEventA>(1, 2.4);

由于对专门功能模板的限制,它可能会变得更复杂一些。此外,您可能需要一个泛型,这意味着错误的参数包将是运行时错误(但很明显)。

我不确定这实际上有什么优势

x.onEventA(1, 2.4);
于 2013-01-16T04:59:14.083 回答
0

没有办法通过保持相同的设计来做你想做的事(即基于运行时计算的整数值调度的事件)。的值eventId仅在运行时知道并确定要调用的函数的签名,但参数的类型必须在编译时知道。直接或间接地,您的函数必须如下所示:

if (eventId == eEventA) { eventA(eventData...); }
else if (eventId == eEventB) { eventB(eventData...); }
...

但这永远不会 compile,因为编译器会解析每个分支,if并且肯定会找到一些eventX与其签名不兼容的调用。

我建议你一开始不要使用这种设计,而是采用类似于 Boost.Signal 的方法来处理事件。

于 2013-01-15T17:06:36.880 回答
0

我不确定我是否完全理解您要做什么,但是像这种模板专业化这样的东西会起作用吗?

#include <iostream>

// ----------------------------------
class EventA
{
public:
    int a;
    double b;
};

class EventB
{
public:
    std::string strEvent;
};

class EventC
{
public:
    unsigned long a;
};

// -------------------------
class X
{
public:
    template<typename EVENT_TYPE> void fsm (const EVENT_TYPE &eventData);

public:
    void eventA (int a, double b) {}
    void eventB (std::string a) {}
    void eventC (unsigned long a) {}
};


// -------------------------
template<typename TYPE>
struct invoker {};

template<>
struct invoker<EventA>
{
    static void call (X *x, EventA e)
    {
        x->eventA (e.a, e.b);
    }
};

template<>
struct invoker<EventB>
{
    static void call (X *x, EventB e)
    {
        x->eventB (e.strEvent);
    }
};

template<>
struct invoker<EventC>
{
    static void call (X *x, EventC e)
    {
        x->eventC (e.a);
    }
};


// ---------------------------------------
template<typename EVENT_TYPE> 
void X::fsm (const EVENT_TYPE &eventData)
{
    invoker<EVENT_TYPE>::call (this, eventData);
}


int main (int argc, char *argv[])
{
    X x;

    EventA eEventA;
    eEventA.a = -1;
    eEventA.b = 2.0;

    EventB eEventB;
    eEventB.strEvent = "xyz";

    EventC eEventC;
    eEventC.a = 42;

    x.fsm (eEventA);
    x.fsm (eEventB);
    x.fsm (eEventC);

    return 0;
}
于 2013-01-15T19:19:24.190 回答