2014-09-29 74 views
3

測試預處理器符號是否定義的常用方法是使用#ifdef。但是,#ifdef不能在宏中使用。我需要的是一種檢查宏的方法,如果該宏的參數是定義的預處理器符號。測試宏內是否定義了預處理器符號

例如:

#define TRACE(x,y) if(IS_DEFINED(x)){ std::cout << y; } 

這裏,TRACE採用兩個參數,第一x應該是一個預處理程序符號的名稱。如果定義了這樣的符號,應該打印第二個參數。不存在的IS_DEFINED函數/宏是我正在尋找的。

的使用情況如下:

#undef BLA 
TRACE(BLA,"abc") // "abc" won't be printed, as BLA is not defined 
#define BLA 1 
TRACE(BLA,"xyz") // "xyz" will be printed, as BLA is a defined symbol 

有沒有辦法來實現這一目標?也許一些宏觀魔法?當然,這個解決方案應該適用於任何符號,不僅僅是BLA或者是一組硬編碼的符號。如果預先知道要檢查的一組符號,顯然很容易。

+1

你可以使用一個變量,並依靠編譯器來優化條件嗎? – BonzaiThePenguin 2014-09-29 12:27:12

+0

@Bonzai企鵝:不確定你的意思。請舉例。 – gexicide 2014-09-29 12:28:35

回答

1

如果你可以讓BLA總是定義爲01(或其他值轉換爲bool),你可以做

#define TRACE(x, y) if (x) { std::cout << y << std::endl; } 

一個體面的編譯器會優化if常量表達式,因此這種方法不會造成高架。

更新:代碼可能無法定義的宏:

#define IS_DEFINED(x) IS_DEFINED2(x) 
#define IS_DEFINED2(x) (#x[0] == 0 || (#x[0] >= '1' && #x[0] <= '9')) 
#define TRACE(x, y) if (IS_DEFINED(x)) { std::cout << y << std::endl; } 

Demo

請注意,它只能如果FOO沒有定義或定義爲空,或者定義一些數目。

它是如何工作
對於函數宏的擴張是這樣的:首先是宏觀參數被擴展,除非它們與###使用,那麼宏本身就是擴大(見正式解釋的章節C++標準的16.3.1或C標準的6.10.3.1)。

所以IS_DEFINED(x)「致電」IS_DEFINED2(x)與擴大宏x。如果定義了x,它將被替換爲定義的任何內容,否則它將按原樣傳遞。

如果您直接撥打IS_DEFINED2(FOO),那麼#x將始終等於"FOO"這不是您想要的。

+0

是的,這很簡單。這也是我目前正在做的。但是,這很不方便。有一個適用於未定義符號的解決方案會使事情變得更容易。 – gexicide 2014-09-29 12:56:27

+0

@gexicide檢查我的更新 – 2014-09-29 13:26:53

+0

哇,那看起來不錯。但它是如何工作的?爲什麼我們需要另一個'IS_DEFINED2'?我知道它看起來像扎堆,但我仍然無法完全破譯它:D。 – gexicide 2014-09-29 13:36:38

4

示例代碼:

#include <iostream> 

#define TRACE(name, msg) TRACE_EVAL_(TRACE_DO_, name, msg) 
#define TRACE_EVAL_(macro, ...) macro(__VA_ARGS__) 
#define TRACE_DO_(name, msg) \ 
    (#name[0] == 0 || #name[0] == '1' ? (void)(std::cout << (msg)) : (void)0) 

#undef FOO 
#define BAR 
#define BAZ 1 

int main() { 
    TRACE(FOO, "foo\n"); 
    TRACE(BAR, "bar\n"); 
    TRACE(BAZ, "baz\n"); 
} 

調整測試中以適合宏定義的可能值TRACE_DO_。請注意,支持非數字值的定義可能會有問題,因爲從宏名稱中很難區分它們...

+0

只適用於'#define FOO '。例如,對於'#define FOO yes'不起作用。名義動物是對的。 – hmijail 2016-03-26 18:27:52

3

字符串化宏(名稱)比較,以宏的字符串化(展開)值:

#include <iostream> 
#include <cstring> 

#define TRACE_STRINGIFY(item) "" #item 
#define TRACE(macro, message)       \ 
    do {            \ 
     if (strcmp("" #macro, TRACE_STRINGIFY(macro))) \ 
      std::cout << message << "\n";    \ 
    } while (0) 

"" # macro擴展爲宏名稱作爲一個字符串,而TRACE_STRINGIFY(macro)第一擴展宏,然後stringifies的結果。如果兩者不同,則macro必須是預處理器宏。

這種方法對於自己定義的宏,即#define FOO FOO確實失敗。這些宏不會被檢測爲預處理器宏。

大多數編譯器應該能夠完全優化兩個字符串文字的比較。 GNU GCC(g ++)4.8.2甚至可以使用-O0(就像C的gcc一樣 - 同樣的方法在C中也很明顯)。

這種方法適用於類函數宏,但只適用於保留括號(和適當數量的逗號,如果宏需要多個參數)並且沒有爲其自身定義(例如#define BAR(x) BAR(x))。

例如:

#define TEST1 TEST1 

#define TEST3 
#define TEST4 0 
#define TEST5 1 
#define TEST6 "string" 
#define TEST7 "" 
#define TEST8 NULL 
#define TEST9 TEST3 
#define TEST10 TEST2 
#define TEST11(x) 

#define TEST13(x,y,z) (x, y, z) 


int main(void) 
{ 
    TRACE(TEST1, "TEST1 is defined"); 
    TRACE(TEST2, "TEST2 is defined"); 
    TRACE(TEST3, "TEST3 is defined"); 
    TRACE(TEST4, "TEST4 is defined"); 
    TRACE(TEST5, "TEST5 is defined"); 
    TRACE(TEST6, "TEST6 is defined"); 
    TRACE(TEST7, "TEST7 is defined"); 
    TRACE(TEST8, "TEST8 is defined"); 
    TRACE(TEST9, "TEST9 is defined"); 
    TRACE(TEST10, "TEST10 is defined"); 
    TRACE(TEST11, "TEST11 is defined"); 
    TRACE(TEST12, "TEST12 is defined"); 
    TRACE(TEST13, "TEST13 is defined"); 
    TRACE(TEST14, "TEST14 is defined"); 

    TRACE(TEST1(), "TEST1() is defined"); 
    TRACE(TEST2(), "TEST2() is defined"); 
    TRACE(TEST3(), "TEST3() is defined"); 
    TRACE(TEST4(), "TEST4() is defined"); 
    TRACE(TEST5(), "TEST5() is defined"); 
    TRACE(TEST6(), "TEST6() is defined"); 
    TRACE(TEST7(), "TEST7() is defined"); 
    TRACE(TEST8(), "TEST8() is defined"); 
    TRACE(TEST9(), "TEST9() is defined"); 
    TRACE(TEST10(), "TEST10() is defined"); 
    TRACE(TEST11(), "TEST11() is defined"); 
    TRACE(TEST12(), "TEST12() is defined"); 
    TRACE(TEST13(,,), "TEST13(,,) is defined"); 
    TRACE(TEST14(,,), "TEST14(,,) is defined"); 

    return 0; 
} 

其輸出

TEST3 is defined 
TEST4 is defined 
TEST5 is defined 
TEST6 is defined 
TEST7 is defined 
TEST8 is defined 
TEST9 is defined 
TEST10 is defined 
TEST3() is defined 
TEST4() is defined 
TEST5() is defined 
TEST6() is defined 
TEST7() is defined 
TEST8() is defined 
TEST9() is defined 
TEST10() is defined 
TEST11() is defined 
TEST13(,,) is defined 

換句話說,該TEST1符號不被識別爲定義(因爲它被定義爲本身),也不是TEST11TEST13而不括號。這是這種方法的侷限性。

使用括號表單適用於所有無參數宏(除TEST1之外,即爲自己定義的那些宏)和所有單參數宏。如果宏需要多個參數,則需要使用正確數量的逗號,否則(例如,如果嘗試TRACE(TEST13(), "..."))會得到編譯時錯誤:「宏TEST13需要3個參數,只有1個參數爲」「或類似的。

有問題?

+0

有趣,但你怎麼知道字符串文字的比較在編譯時被優化了? (一些硬引用是好的,彙編程序檢查是軼事) – hmijail 2016-03-26 09:53:35

+0

那麼,我能找到的最好的參考是關於builtin的GCC手冊頁並沒有說真正具體的東西。但是,GCC 5.2 -O0和Clang 3.8 -O1在編譯時都會評估strcmp。這對於嵌入式開發非常有用!我寫了一篇關於在普通C中以更通用的方式使用它的博客文章:http://hmijailblog.blogspot.com/2016/03/an-isdefined-c-macro-to-check-whether.html – hmijail 2016-03-26 19:38:27

+0

@hmijail:我喜歡博客文章中的'__builtin_strcmp()'方法;它在編譯時被評估的可能性最高。一般來說,'cpp'(C預處理器)缺少字符串比較很煩人,而且有些令人驚訝。但是,如果我們要限制自己的* numeric *觸發器/規則宏,一切都會更加簡單。我特別喜歡GNU C庫使用'#define _POSIX_C_SOURCE YYYYMML'來控制導出哪個版本的POSIX C功能的方式。 (「L」是爲了使它總是「長」。) – 2016-04-01 14:11:32

1

基於Christoph答案的替代版本是我允許將可變參數傳遞給日誌記錄方法(在本例中爲Objective-C,但也適用於C++)。

#define TGLog(tag, msg, ...) TGLog_eval_(TGLog_do_, tag, msg, ## __VA_ARGS__) 
#define TGLog_eval_(macro, ...) macro(__VA_ARGS__) 
#define TGLog_do_(tag, msg, ...) \ 
(#tag[0] == 0 || #tag[0] == '1') ? NSLog(@"%s",[NSString stringWithFormat:msg, ## __VA_ARGS__]) : (void)0; 

在variadic參數變量前添加的##刪除前面的逗號,如果根本沒有參數的話。這確保了擴展不會由於尾隨逗號而失敗。