【问题标题】:Expansion of Nested Macros with reinterpret cast in c++在 c++ 中使用重新解释转换扩展嵌套宏
【发布时间】:2016-04-14 10:44:38
【问题描述】:

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

另外,当我尝试编译代码时,我遇到了错误

需要下面代码的解释。

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

提前致谢

电机.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:\Users\xprk569\StateMachine\main.cpp -o obj\Debug\main.o
In file included from C:\Users\xprk569\StateMachine\main.cpp:2:0:
C:\Users\xprk569\StateMachine\Motor.h: In member function 'virtual const StateStruct* Motor::GetStateMap()':
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:\Users\xprk569\StateMachine\Motor.h:29:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_Idle)
         ^
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:\Users\xprk569\StateMachine\Motor.h:30:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_Stop)
         ^
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:\Users\xprk569\StateMachine\Motor.h:31:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_Start)
         ^
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
     { reinterpret_cast<StateFunc>(entry) },
                                        ^
C:\Users\xprk569\StateMachine\Motor.h:32:9: note: in expansion of macro 'STATE_MAP_ENTRY'
         STATE_MAP_ENTRY(ST_ChangeSpeed)
         ^
C:\Users\xprk569\StateMachine\StateMachine.h:43:39: error: invalid cast from type 'int' to type 'StateFunc {aka void (StateMachine::*)(EventData*)}'
     { reinterpret_cast<StateFunc>(NULL) }\
                                       ^
C:\Users\xprk569\StateMachine\Motor.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 和 END_STATE_MAP 是什么” - “是下面头文件中定义的宏。”我猜你自己已经回答过了。
  • 避免宏(不要复制古老的MFC)
  • 是的,没错。它们是宏,但不能将它们编写为使代码易于理解的函数。我的问题是为什么它们在 Motor.h 中设置为标签,将编辑问题:)
  • 无法复制,因为我没有 EventData.h 的副本
  • @Richard:添加 EventData.h。

标签: c++ c++11 macros


【解决方案1】:

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

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

例如

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 statechart。

【讨论】:

  • 为什么STATE_MAP_ENTRY(..)在BEGIN_STATE_MAP和END_STATE_MAP之间,我理解是宏,但为什么会这样定义?
  • @user2256825 我猜它是几年前在 Visual Studio 5 或 6 下编写的?在那个时候,c++ 是一种非常不发达的语言,实现者不得不求助于恶作剧。
【解决方案2】:

因此,您很快就会了解为什么宏在可读的 C++ 中是禁忌。如果遇到错误,您必须展开宏以识别错误所在,而且在大多数 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: 错误:无效使用成员(你忘了&amp; 吗?)
{ reinterpret_cast&lt;StateFunc&gt;(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_casts 将始终返回一个指向该方法的指针调用无效,这意味着整个宏块充其量是毫无意义的,最坏的情况是有毒的。在当前状态下,您也可以不使用这些宏,或者如果您需要定义方法,只需执行以下操作:

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,它将是StateStructs的数组
  4. 在第一次调用GetStateMap 方法时,StateMap 将被初始化为包含指向ST_IdleST_StopST_StartST_ChangeSpeedreinterpret_cast 到@987657 的方法指针@s
  5. 定义GetStateMap 以返回StateMap 数组

【讨论】:

  • 谢谢,但我不明白这个语法的作用 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。我理解函数指针,但是用户理解上面 5 行是什么,begin 和 end 做什么,为什么函数 ptrs 在 begin 和 end 里面,为什么不能在外面,只是想了解上面 5 行的语法跨度>
  • @user2256825 我已经编辑以通过扩展这些宏来解释它们的作用。希望您需要在stackoverflow.com 上提出问题以了解宏这一事实会阻止您使用它们,因为正如多次声明的那样,宏难以阅读、使用、维护和解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-19
  • 2011-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多