2017-05-15 222 views
1

我試了一個多星期沒有成功。將枚舉轉換爲枚舉的MACRO

我在兩個處理器之間創建一個記錄器接口,我需要幫助定義自動化的MACROS。

我的意思是? 假設我有一個記錄器消息定義爲LOGGER_MSG_ID_2,它採用uint8和uint16類型的兩個參數。

我有一個枚舉定義爲:

typedef enum{ 
    PARAM_NONE, 
    PARAM_SIZE_UINT8, 
    PARAM_SIZE_UINT16, 
    PARAM_SIZE_UINT32 
}paramSize_e; 

所以LOGGER_MSG_ID_2將具有位圖定義爲:

#define LOGGER_MSG_ID_2_BITMAP (PARAM_SIZE_UINT16 << 2 | PARAM_SIZE_UINT8) 

此位圖是1點字節的大小,所以參數的最大數目是4。 稍後,我有一個根據消息ID定義所有參數類型的列表:

#define ID_2_P0_TYPE        uint8 // first parameter 
#define ID_2_P1_TYPE        uint16 // 2nd parameter 
#define ID_2_P2_TYPE        0  // 3rd parameter 
#define ID_2_P3_TYPE        0  // 4th parameter 

正如我所說,我有4個參數的限制,所以我想定義它們,讓MACRO決定使用它們的天氣。我將它們定義爲0,但它可以是任何有效的。

我有其他的MACROS使用位圖來獲取所有類型的屬性,如參數數量和消息大小。

現在這是棘手的部分。我想建立一個MACRO,根據類型創建一個位圖。原因是我不想在位圖和參數定義之間進行冗餘。 我的問題是,我試過的一切都無法編譯。

最後,我想有一個宏,如:

#define GET_ENUM_FROM_TYPE(_type) 

,讓我PARAM_SIZE_UINT8,PARAM_SIZE_UINT16或PARAM_SIZE_UINT32根據類型。

限制:我在Windows(armcl.exe)和C99上使用arm編譯器。我不能使用C11 Generic()

我試過如下:

#define GET_ENUM_FROM_TYPE(_type)  \ 
(_type == uint8) ? PARAM_SIZE_UINT8 : \ 
     ((_type == uint16) ? PARAM_SIZE_UINT16 : \ 
       ((_type == uint32) ? PARAM_SIZE_UINT32 : PARAM_NONE)) 

最後,我想用它喜歡:

#define LOGGER_MSG_ID_2_BITMAP    \ 
    (GET_ENUM_FROM_TYPE(ID_2_P3_TYPE) << 6 | \ 
    GET_ENUM_FROM_TYPE(ID_2_P2_TYPE) << 4 | \ 
    GET_ENUM_FROM_TYPE(ID_2_P1_TYPE) << 2 | \ 
    GET_ENUM_FROM_TYPE(ID_2_P0_TYPE)) 

但是,當我使用它,它不會編譯。

我有位圖的表:

uint8 paramsSizeBitmap [] = { 
    LOGGER_MSG_ID_1_BITMAP,       /* LOGGER_MSG_ID_1 */ 
    LOGGER_MSG_ID_2_BITMAP,       /* LOGGER_MSG_ID_2 */ 
    LOGGER_MSG_ID_3_BITMAP,       /* LOGGER_MSG_ID_3 */ 
    LOGGER_MSG_ID_4_BITMAP,       /* LOGGER_MSG_ID_4 */ 
    LOGGER_MSG_ID_5_BITMAP,       /* LOGGER_MSG_ID_5 */ 
    LOGGER_MSG_ID_6_BITMAP,       /* LOGGER_MSG_ID_6 */ 
    LOGGER_MSG_ID_7_BITMAP,       /* LOGGER_MSG_ID_7 */ 
    LOGGER_MSG_ID_8_BITMAP,       /* LOGGER_MSG_ID_8 */ 
    LOGGER_MSG_ID_9_BITMAP,       /* LOGGER_MSG_ID_9 */ 
    LOGGER_MSG_ID_10_BITMAP,      /* LOGGER_MSG_ID_10 */ 
}; 

而且我得到這個錯誤:

line 39: error #18: expected a ")" 
line 39: error #29: expected an expression 

(線39 LOGGER_MSG_ID_2_BITMAP

我在哪裏去了?

----- -----編輯

現在我有一個解決辦法,我真的不喜歡。 我不使用UINT64,所以我做了一個使用sizeof() MACRO的,現在我的MACRO看起來是這樣的:

#define GET_ENUM_FROM_TYPE(_type)  \ 
(sizeof(_type) == sizeof(uint8)) ? PARAM_SIZE_UINT8 : \ 
     ((sizeof(_type) == sizeof(uint16)) ? PARAM_SIZE_UINT16 : \ 
       ((sizeof(_type) == sizeof(uint32)) ? PARAM_SIZE_UINT32 : PARAM_NONE)) 

和我paraemters名單是:

#define NO_PARAM         uint64 

#define ID_2_P0_TYPE        uint8 
#define ID_2_P1_TYPE        uint16 
#define ID_2_P2_TYPE        NO_PARAM 
#define ID_2_P3_TYPE        NO_PARAM 

它工作正常,但...你知道...

+0

您是否在預處理後檢查了表的外觀?大多數編譯器都提供了輸出預處理源文件的選項。 – user694733

+1

另外,像'GET_ENUM_FROM_TYPE'這樣的不重要的宏應該被包裝在圓括號中。運營商的優先權可能會在這裏咬你。 – user694733

+0

「這個位圖是1個字節大小」那麼你爲什麼使用uint16?沒有多大意義。 – Lundin

回答

3

我相信解決方案是使用連接運算符##和幫助器定義。

// These must match your enum 
#define HELPER_0  PARAM_NONE 
#define HELPER_uint8 PARAM_SIZE_UINT8 
#define HELPER_uint16 PARAM_SIZE_UINT16 
#define HELPER_uint32 PARAM_SIZE_UINT32 

// Secondary macro to avoid expansion to HELPER__type 
#define CONCAT(a, b) a ## b 

// Outer parenthesis not strictly necessary here 
#define GET_ENUM_FROM_TYPE(_type) (CONCAT(HELPER_, _type)) 

隨着該GET_ENUM_FROM_TYPE(ID_2_P1_TYPE)將預處理後擴大到(PARAM_SIZE_UINT16)

請注意,HELPER_***定義中的後綴必須完全匹配ID_*_P*_TYPE宏的內容。例如HELPER_UINT8將不起作用(無效情況)。 (謝謝@ cxw)

+0

非常好!我建議在答案中明確指出HELPER_定義的區分大小寫,但HELPER_UINT8不起作用。我經常看不到混合大小寫的預處理器符號。 – cxw

+0

@cxw好點。添加。 – user694733

+0

拯救生命! 不知道##操作符!我有很多東西可以使用它! – DrorNohi

2

的基本問題是==不支持類型,只爲。鑑於

uint8 foo; 

你可以說foo==42但不foo == uint8。這是因爲類型不是C中的第一類。

一個黑客可能會使用C預處理器字符串操作符#gcc docs)。但是,這會將所有計算移至運行時,可能不適用於嵌入式環境。例如:

#define GET_ENUM_FROM_TYPE(_type) ( \ 
(strcmp(#_type, "uint8")==0) ? PARAM_SIZE_UINT8 : \ 
     ((strcmp(#_type, "uint16")==0) ? PARAM_SIZE_UINT16 : \ 
       ((strcmp(#_type, "uint32")==0) ? PARAM_SIZE_UINT32 : PARAM_NONE)) \ 
) 

有了這個定義,

GET_ENUM_FROM_TYPE(uint8) 

擴展到

((strcmp("uint8", "uint8")==0) ? PARAM_SIZE_UINT8 : ((strcmp("uint8", "uint16")==0) ? PARAM_SIZE_UINT16 : ((strcmp("uint8", "uint32")==0) ? PARAM_SIZE_UINT32 : PARAM_NONE))) 

應該在運行時做你想要什麼,雖然。

+0

哇。尼斯黑客! 但不幸的是你是對的。我必須對它進行預處理。 – DrorNohi

+0

@DrorNohi你打開一個額外的預處理步驟,還是隻需要在C?我維護一個[基於Perl的預處理器](https://github.com/d-ash/perlpp),可能會有用。 – cxw

+0

@DrorNohi其實,我認爲user694733釘了它:)。快樂黑客! – cxw

0

對不起,這不直接回答這個問題。但是你應該重新考慮這整個代碼。

首先,_Generic會解決這個優雅。

對於像這些宏一樣的宏組合,使用所謂的X macros是很好的選擇,它適用於「我不想在位圖和參數定義之間進行冗餘」等情況。你可能會用X宏重寫你的代碼,並去掉許多多餘的定義和宏。另一個故事將會如何可讀。

但是,無論什麼時候你發現自己深陷於一些宏觀元編程叢林之中,它幾乎總是表明程序設計不佳。所有這些聞起來像是一個人爲的解決方案,可以用更好的方式解決 - 這是一個"XY problem"。 (不要與X宏混淆:))。最好的解決方案很可能涉及以更簡單的方式完全重寫。你的情況在任何方面聽起來都不是唯一的,看起來你只是想生成一堆位掩碼。

好的程序員總是試圖讓他們的代碼更簡單,而不是使它更復雜。


此外,由於C語言類型系統的影響,您可能在整個代碼中出現或多或少嚴重的錯誤。它可以概括爲:

  • 位移或其他位運算不應該用於簽名類型。這樣做會導致各種微妙的錯誤和不明確的行爲。
  • 枚舉常量始終爲有簽名的int類型。您應該避免將它們與按位算術混合。完全避免像這樣的程序枚舉。
  • 小整數類型(如uint8_tuint16_t)在表達式中使用時隱式提升爲int。這意味着C語言將帶動你嘗試獲得正確類型的大部分嘗試,並用int取代所有的東西。
  • 您的宏的結果類型將是int,這不是您想要的。
+0

不幸的是我的編譯器不支持_Generic。 – DrorNohi