2016-04-14 32 views
0

我偶然發現了下面的代碼,並且發現它理解嵌套宏和類型強制轉換很複雜。在C++中使用reinterpret強制擴展嵌套宏

而且當我試圖編譯代碼,我遇到了一個錯誤

將需要下面的代碼的explanantion。

爲什麼BEGIN_STATE_MAP和END_STATE_MAP設置爲Motor.h標籤,這的確是新的我提前

感謝

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 

當我試圖編譯代碼above.Below是遇到的錯誤

錯誤

-------------- 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和 中聲明爲什麼拋出錯誤?

在此先感謝

+0

「什麼是BEGIN_STATE_MAP和END_STATE_MAP」 - 「都在被定義的宏低頭文件「。我想你已經回答了你自己。 –

+1

避免宏(不要複製古代的MFC) –

+0

是的,這是正確的。它們是宏,但不能將它們編寫爲使代碼易於理解的函數。我的問題是爲什麼他們在Motor.h中設置爲標籤,將編輯問題:) – user2256825

回答

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狀態圖。

+0

爲什麼STATE_MAP_ENTRY(..)在BEGIN_STATE_MAP和END_STATE_MAP之間,我明白它是宏,但它爲什麼定義這種方式? – user2256825

+0

@ user2256825我會猜測這是幾年前在visual studio 5或6下編寫的?早在那時,C++就是一種非常不發達的語言,實現者不得不採取詭計。 –

0

所以你很快就會知道爲什麼宏在可讀的C++中是沒有用的。如果出現錯誤,則必須將宏展開以確定錯誤的位置,而且在大多數IDE中都無法對其進行調試。

反正是說讓去的擴大,他們都是一樣的錯誤,所以我們只要看一下第一種:

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) },

所以這是抱怨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陣列
+0

謝謝,但我不明白這是什麼語法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。我理解函數指針,但是dint用戶站在上面5行,什麼是開始和結束,爲什麼函數ptrs在裏面開始和結束,爲什麼不能在外面,只想瞭解上面5行的語法 – user2256825

+0

@ user2256825我編輯過來解釋這些宏是通過展開它們來完成的。希望事實上,你需要在http://www.stackoverflow.com上提出一個問題來理解宏,這會阻止你使用它們,因爲宏已經多次指出,很難閱讀,使用,維護和說明。 –