用c++中的重新解释强制转换扩展嵌套宏

Expansion of Nested Macros with reinterpret cast in c++

本文关键字:转换 扩展 嵌套 解释 c++ 新解释      更新时间:2023-10-16

我偶然发现了下面的代码,并发现理解其中的嵌套宏和类型转换非常复杂。

此外,当我试图编译代码时,我遇到了一个错误

需要对以下代码进行解释。

为什么在Motor.h中将BEGIN_STATE_MAP和END_STATE_MAP设置为标签,这对我来说真的很新鲜

提前感谢

Motor.h

// the Motor state machine class
class Motor : public StateMachine
{
public:
    Motor() : StateMachine(ST_MAX_STATES) {}
    // external events taken by this state machine
    void Halt();
    void SetSpeed(MotorData*);
private:
    // state machine state functions
    void ST_Idle();
    void ST_Stop();
    void ST_Start(MotorData*);
    void ST_ChangeSpeed(MotorData*);
    // state map to define state function order
    BEGIN_STATE_MAP
        STATE_MAP_ENTRY(ST_Idle)
        STATE_MAP_ENTRY(ST_Stop)
        STATE_MAP_ENTRY(ST_Start)
        STATE_MAP_ENTRY(ST_ChangeSpeed)
    END_STATE_MAP
    // state enumeration order must match the order of state
    // method entries in the state map
    enum E_States {
        ST_IDLE = 0,
        ST_STOP,
        ST_START,
        ST_CHANGE_SPEED,
        ST_MAX_STATES
    };
};
#endif //MOTOR_H

什么是BEGIN_STATE_MAP和END_STATE_MAP,我发现这个定义真的很新,BEGIN_STATE_MAP和END_STATE_MAP是在下面的头文件中定义的宏。

StateMachine.h

#ifndef STATE_MACHINE_H
#define STATE_MACHINE_H
#include <stdio.h>
#include "EventData.h"
struct StateStruct;
// base class for state machines
class StateMachine
{
public:
    StateMachine(int maxStates);
    virtual ~StateMachine() {}
protected:
    enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN };
    unsigned char currentState;
    void ExternalEvent(unsigned char, EventData* = NULL);
    void InternalEvent(unsigned char, EventData* = NULL);
    virtual const StateStruct* GetStateMap() = 0;
private:
    const int _maxStates;
    bool _eventGenerated;
    EventData* _pEventData;
    void StateEngine(void);
};
typedef void (StateMachine::*StateFunc)(EventData *);
struct StateStruct
{
    StateFunc pStateFunc;
};
#define BEGIN_STATE_MAP 
public:
const StateStruct* GetStateMap() {
    static const StateStruct StateMap[] = {
#define STATE_MAP_ENTRY(entry)
    { reinterpret_cast<StateFunc>(entry) },
#define END_STATE_MAP 
    { reinterpret_cast<StateFunc>(NULL) }
    }; 
    return &StateMap[0]; }
#define BEGIN_TRANSITION_MAP 
    static const unsigned char TRANSITIONS[] = {
#define TRANSITION_MAP_ENTRY(entry)
    entry,
#define END_TRANSITION_MAP(data) 
    0 };
    ExternalEvent(TRANSITIONS[currentState], data);
#endif

EventData.h

#ifndef EVENT_DATA_H
#define EVENT_DATA_H
class EventData
{
public:
   virtual ~EventData() {};
};
#endif //EVENT_DATA_H

当我试图编译上面的代码时。以下是遇到的错误

错误

-------------- Build: Debug in StateMachine (compiler: GNU GCC Compiler)---------------
mingw32-g++.exe -Wall -fexceptions -g -pedantic -Wzero-as-null-pointer-constant -std=c++0x -Wextra -Wall  -c C:Usersxprk569StateMachinemain.cpp -o objDebugmain.o
In file included from C:Usersxprk569StateMachinemain.cpp:2:0:
C:Usersxprk569StateMachineMotor.h: In member function 'virtual const StateStruct* Motor::GetStateMap()':
C:Usersxprk569StateMachineStateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:Usersxprk569StateMachineMotor.h:29:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_Idle)
         ^
C:Usersxprk569StateMachineStateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:Usersxprk569StateMachineMotor.h:30:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_Stop)
         ^
C:Usersxprk569StateMachineStateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:Usersxprk569StateMachineMotor.h:31:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_Start)
         ^
C:Usersxprk569StateMachineStateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:Usersxprk569StateMachineMotor.h:32:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_ChangeSpeed)
         ^
C:Usersxprk569StateMachineStateMachine.h:43:39: error: invalid cast from type 'int' to type 'StateFunc {aka void (StateMachine::*)(EventData*)}'
     { reinterpret_cast<StateFunc>(NULL) }
                                       ^
C:Usersxprk569StateMachineMotor.h:33:5: note: in expansion of macro 'END_STATE_MAP'
     END_STATE_MAP
     ^
Process terminated with status 1 (0 minute(s), 0 second(s))
5 error(s), 0 warning(s) (0 minute(s), 0 second(s))

有人能解释一下为什么Motor.h中的宏是这样写的吗,为什么它在StateMachine.h中是这样声明的,以及为什么抛出错误?

提前感谢

这段代码似乎依赖于一些非标准的编译器扩展/错误。

要编译它(不知道它是否真的能工作),你需要用完全限定的成员函数指针替换函数名:

例如

BEGIN_STATE_MAP
    STATE_MAP_ENTRY(&Motor::ST_Idle)
    STATE_MAP_ENTRY(&Motor::ST_Stop)
    STATE_MAP_ENTRY(&Motor::ST_Start)
    STATE_MAP_ENTRY(&Motor::ST_ChangeSpeed)
END_STATE_MAP

之后,您需要找到一种方法来克服不合格的铸件:

/tmp/gcc-explorer-compiler116314-75-1uiyu0/example.cpp: In member function 'virtual const StateStruct* Motor::GetStateMap()':
44 : error: invalid cast from type 'long int' to type 'StateFunc {aka void (StateMachine::*)(EventData*)}'
{ reinterpret_cast<StateFunc>(NULL) }
^
83 : note: in expansion of macro 'END_STATE_MAP'

这个演员阵容是完全非法的。如果我是你,我会把代码扔进垃圾桶并重写——或者使用一个经过验证的状态机框架,比如boost元状态机或boost状态图。

因此,您将很快了解为什么宏在可读C++中不是nos。如果出现错误,则必须展开宏以确定错误所在的位置,而且在大多数IDE中也无法对其进行调试。

不管怎么说,让我们开始扩展,他们都有同样的错误,所以我们只看第一个:

C: \Users\xprk569\StateMachine\Motor.h:29:9:注意:在宏STATE_MAP_ENTRY的扩展中
STATE_MAP_ENTRY(ST_Idle)
C: \Users\xprk569\StateMachine\StateMachine.h:40:40:错误:成员使用无效(您忘记&了吗?)
{ reinterpret_cast<StateFunc>(entry) },

所以这是在抱怨第29行:STATE_MAP_ENTRY(ST_Idle),所以让我们扩展一下:

{ reinterpret_cast<StateFunc>(entry) },

显然,在BEGIN_STATE_MAPEND_STATE_MAP的作用域之外,这是一种糟糕的语法,因此在调试许多宏时,您还必须查看作用域宏。。。不幸的是,有时它们可能没有被清楚地命名或描绘,但让我们先定义好我们出错的那条线。我们想把这个StateFunc演成什么?

typedef void (StateMachine::*StateFunc)(EventData *);

它是一个指向成员函数的指针,该函数返回void并接受EventData *。警铃应该响了。你不能听之任之!ST_Idle的格式为:void (StateMachine::*)(),因此不能强制转换为void (StateMachine::*StateFunc)(EventData *)。对于传递到宏中的所有函数来说,这也是同样的问题——它们都不返回void,也不接受EventData*,所以即使修复了语法,这些reinterpret_cast也总是会返回一个指向无效调用方法的指针,这意味着整个宏块往好里说是毫无意义的,往坏里说是有害的。在当前状态下,您可以不使用这些宏,或者如果您需要定义方法,只需执行:

BEGIN_STATE_MAP
END_STATE_MAP

但是,如果您要将方法声明更改为类似的内容

void ST_Idle(EventData*);

然后您需要使用以下语法:

STATE_MAP_ENTRY(&Motor::ST_Idle)

如果你不了解方法指针,它们会非常复杂。我在这里打了一个简单的例子:http://ideone.com/nL0HnQ欢迎提问。

编辑:

要在这里扩展宏,我们将获得:

public: // BEGIN_STATE_MAP 
const StateStruct* GetStateMap() { // BEGIN_STATE_MAP 
    static const StateStruct StateMap[] = { // BEGIN_STATE_MAP 
    { reinterpret_cast<StateFunc>(ST_Idle) } // STATE_MAP_ENTRY(ST_Idle)
    { reinterpret_cast<StateFunc>(ST_Stop) } // STATE_MAP_ENTRY(ST_Stop)
    { reinterpret_cast<StateFunc>(ST_Start) } // STATE_MAP_ENTRY(ST_Start)
    { reinterpret_cast<StateFunc>(ST_ChangeSpeed) } // STATE_MAP_ENTRY(ST_ChangeSpeed)
    { reinterpret_cast<StateFunc>(NULL) } // END_STATE_MAP
    }; // END_STATE_MAP
    return &StateMap[0]; } // END_STATE_MAP

因此,这组宏将:

  1. 将作用域设置为public
  2. 声明方法GetStateMap
  3. StateMap声明为GetStateMap的静态本地,它将是StateStruct的数组
  4. GetStateMap方法的第一次调用中,StateMap将被初始化为包含指向ST_IdleST_StopST_StartST_ChangeSpeedNULLreinterpret_castStateFunc的方法指针
  5. 定义GetStateMap返回StateMap数组