【问题标题】:FSM Using Member Function Pointers in C++FSM 在 C++ 中使用成员函数指针
【发布时间】:2014-05-28 15:08:32
【问题描述】:

我有两个问题:第一个是关于我要在这里解决的特定错误的原因,第二个是关于我解决问题的方法的有效性。

我正在尝试创建一个使用成员函数指针的状态机,但我迷失在细节中。其他关于成员函数指针主题的 Q/As 似乎处理从类外部调用成员函数,并提倡使用 std::function 和 std::bind。我还阅读了the C++ FAQ on pointers to member functions,它还提供了在类外调用成员函数的示例。我不完全确定这些示例是否适用于我正在尝试做的事情。

这种状态机方法在我试图为这个嵌入式应用程序做的事情中是通用的(这是我用 C++ 编写的第一个真正的应用程序。如果这种方法很愚蠢,请帮助我找到更好的解决方案。) .下面的具体代码是针对 MessageHandler 类的。思路是每个硬件通信端口ISR都会将其内容复制到自己的缓冲区中,然后将自己的MessageHandler对象排队处理。有几个通信接口,但协议都是一样的。

MessageHandler.h 中的一些 sn-ps:

class MessageHandler
{
private:
    enum state_t {
        WAIT_METADATA,
        WAIT_PAYLOAD,
        NUM_STATES
    };
    enum transfer_t {
        INPUT_PARTIAL_METADATA,
        INPUT_METADATA,
        INPUT_PARTIAL_PAYLOAD,
        INPUT_COMMAND,
        INPUT_TIMEOUT,
        INPUT_UNKNOWN,
        NUM_TRANSFER_INPUTS
    };

    typedef transfer_t (MessageHandler::*transferFunction)();
    transferFunction transferFunctionTable[NUM_STATES] = {
        checkMetadata,
        checkPayload
    };

    typedef state_t(MessageHandler::*stateFunction)();
    stateFunction stateFunctionTable[NUM_TRANSFER_INPUTS] = {
        gotPartialMetadata,
        gotMetadata,
        gotPartialPayload,
        gotCommand,
        gotTimeout,
        gotUnknownError
    };

    size_t processTable[NUM_TRANSFER_INPUTS][NUM_STATES] = {
        { 0, 5 },
        { 1, 5 },
        { 5, 2 },
        { 5, 3 },
        { 4, 4 },
        { 5, 5 }
    };

    ...

    state_t state;

    transfer_t getInput();
    transfer_t checkMetadata();
    transfer_t checkPayload();
    state_t gotPartialMetadata();
    state_t gotMetadata();
    state_t gotPartialPayload();
    state_t gotCommand();
    state_t gotTimeout();
    state_t gotUnknownError();

    ...

public:
    ...
    state_t process();

};

还有一些来自 MessageHandler.cpp 的例子:

MessageHandler::transfer_t MessageHandler::getInput()
{
    ...
    return transferFunctionTable[state]();
}

MessageHandler.cpp 中的另一个:

MessageHandler::state_t MessageHandler::process()
{
    do {
        state = stateFunctionTable[ processTable[getInput()][state] ]();
    } while (cbRemaining > 0);
}

我不明白为什么我提供的两个成员函数在函数表调用中出现错误: Error: expression preceding parentheses of apparent call must have (pointer-to-) function type

这不是我提供的吗?也就是 transferFunctionTable 和 stateFunctionTable 的类型怎么不是函数指针呢?

我认为要解决这个问题,我必须将所有函数设为静态并将引用/指针传递给 MessageHandler 对象。

提前感谢您的帮助。

【问题讨论】:

  • 指向成员函数的指针是一场噩梦!
  • 至于您的一般方法:是的,在 OO 语言中肯定有更好的方法来处理 FSM。例如。众所周知的State Pattern。在这里您可以找到一个模板库,它可以让您更轻松地实现基于状态模式的 FSM 方法:STTCL。
  • @πάνταῥεῖ:每个状态的单独类型更像是一种反模式。
  • @BenVoigt 是一个保存成员函数指针的表 ...
  • @πάνταῥεῖ:我同意,每个状态函数应该只返回下一个状态的函数指针(对),而不是一个枚举整数索引必须与枚举保持同步的表。

标签: c++ c pointers c++11 fsm


【解决方案1】:

你的意思是:

MessageHandler::state_t MessageHandler::process()
{
    do {
        stateFunction stateAction = stateFunctionTable[ processTable[getInput()][state] ];
        state = (this->*stateAction)();
    } while (cbRemaining > 0);
}

必须使用.* 或->* 取消引用指向成员的指针。您不能只对它应用函数调用运算符。

另外,您的数组初始化有问题。应该是

transferFunction transferFunctionTable[NUM_STATES] = {
    &MessageHandler::checkMetadata,
    &MessageHandler::checkPayload
};

因为成员函数的裸名不会衰减为指向成员的指针。您必须在限定名称上使用地址运算符&。

当然,这两个修复都需要应用于两个成员函数指针表。

【讨论】:

  • 我认为 Jarod42 的想法是正确的,你上面的内容变成了state = (this->*stateFunctionTable[ processTable[getInput()][state] ])();。 transferFunction 的 typedef 不应该明确地将指针限定为指向成员函数的指针吗?没有它似乎也能工作。
【解决方案2】:
MessageHandler::transfer_t MessageHandler::getInput()
{
    return transferFunctionTable[state]();
}

应该是

MessageHandler::transfer_t MessageHandler::getInput()
{
    return (this->*transferFunctionTable[state])();
}

【讨论】:

    猜你喜欢
    • 2018-09-12
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多