我如何用C++进行自动机/状态机编码

How can I do automata/state machine coding in C++?

本文关键字:自动机 状态机 编码 何用 C++      更新时间:2023-10-16

我在另一种编程语言中使用过它,它非常有用。

我在C++中找不到任何关于这方面的信息。

让我们以以下代码为例:

void change();
enum
{
    end = 0,
    gmx
}
int
    gExitType;
int main()
{
    gExitType = end;
    SetTimer(&change, 10000, 0);
    return 0;
}
void ApplicationExit()
{
    switch (gExitType)
    {
        case end:
            printf("This application was ended by the server");
        case gmx:
            printf("This application was ended by the timer");
    }
    ::exit(0);
}
void change()
{
    gExitType = gmx;
    ApplicationExit();
}

我们在C++中就是这样做的,但当使用状态机/自动机时,我可以在另一种语言中做这样的事情:

void change();
int main()
{
    state exitType:end;
    SetTimer(&change, 10000, 0);
    return 0;
}
void ApplicationExit() <exitType:end>
{
    printf("This application was ended by the server");
}
void ApplicationExit() <exitType:gmx>
{
    printf("This application ended by the timer");
}
void change()
{
    state exitType:gmx;
    ApplicationExit();
}

在我看来,这是一种非常优雅的方式来实现目标。我该如何在C++中做到这一点?这段代码似乎不起作用(很明显,因为我找不到任何与C++相关的自动机)

澄清我的观点:

那么,使用这种技术有什么优势呢?好吧,正如你可以清楚地看到的,代码更小;当然,我在第一个版本中添加了一个枚举,使示例更加相似,但ApplicationExit函数显然更小。它也更明确——你不需要函数中的大型switch语句来确定发生了什么,如果你愿意,你可以将不同的ApplicationExit放在不同的文件中,以独立处理不同的代码集。它还使用较少的全局变量。

有一些C++库,如Boost.statechart,专门尝试为编码状态机提供丰富的支持:
http://www.boost.org/doc/libs/1_54_0/libs/statechart/doc/tutorial.html

除此之外,对某些类型的状态机进行编码的一种非常优雅的方法是将它们定义为例程:
http://c2.com/cgi/wiki?CoRoutine
http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/

C++中不直接支持推论,但有两种可能的方法实现它们:

1) 使用类似于实现duff设备的技术,在此处详细解释:
http://blog.think-async.com/search/label/coroutines
这与C#的迭代器的工作方式非常相似,例如,一个限制是只能从协程调用堆栈中最顶层的函数中生成协程。OTOH,这种方法的优点是协程的每个实例只需要很少的内存。

2) 为每个协程分配一个单独的堆栈和寄存器空间
这本质上使协程成为一个全面的执行线程,唯一的区别是用户对线程调度(也称为协作多任务)负有全部责任。
boost提供了一个可移植的实现:
http://www.boost.org/doc/libs/1_54_0/libs/coroutine/doc/html/coroutine/intro.html

对于这个特定的例子,您可以使用对象和多态性来表示不同的状态。例如:

class StateObject
{
    public:
        virtual void action(void) = 0;
};
class EndedBy : public StateObject
{
    private:
        const char *const reason;
    public:
        EndedBy( const char *const reason_ ) : reason( reason_ ) { }
        virtual void action(void)
        {
            puts(reason);
        }
};
EndedBy EndedByServer("This application was ended by the server");
EndedBy EndedByTimer ("This application ended by the timer");
StateObject *state = &EndedByServer;
void change()
{
    state = &EndedByTimer;
}
void ApplicationExit()
{
    state->action();
    ::exit(0);
}
int main()
{
    SetTimer(&change, 10000, 0);
    // whatever stuff here... 
    // presumably eventually causes ApplicationExit() to get called before return 0;
    return 0;
}

也就是说,这不是一个伟大的设计,也不是一般意义上的FSM。但是,它会满足你的迫切需要。

您可以查找State Pattern(一个参考:http://en.wikipedia.org/wiki/State_pattern),以便对该模式进行更一般的处理。

不过,基本思想是,每个状态都是一些常见"状态"类的子类,您可以使用多态性来确定每个状态所代表的不同操作和行为。指向通用"状态"基类的指针会跟踪您当前所处的状态

状态对象可以是不同的类型,或者如我上面的示例所示,相同对象的不同实例配置不同,或者混合。

您可以在int上使用Template值专门化来实现您想要的目标。

(很抱歉,我在平板电脑上,所以我无法提供示例,我将在周日更新)