gex*_*ide 4 c macros c-preprocessor
测试是否定义了预处理器符号的常用方法是使用#ifdef。但是,#ifdef不能在宏中使用。我需要的是一种检查宏的参数,如果该宏的参数是已定义的预处理程序符号。
例如:
#define TRACE(x,y) if(IS_DEFINED(x)){ std::cout << y; }
Run Code Online (Sandbox Code Playgroud)
在这里,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
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点?也许一些宏魔术?当然,该解决方案应该适用于任何符号,不仅适用于符号,也适用于BLA一组硬编码符号。如果要事先知道要检查的符号集,显然很容易。
Linux'为这个用例kgconfig.h定义了一个__is_defined宏:
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val
/*
* Helper macros to use CONFIG_ options in C/CPP expressions. Note that
* these only work with boolean and tristate options.
*/
/*
* Getting something that works in C and CPP for an arg that may or may
* not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1"
* we match on the placeholder define, insert the "0," for arg1 and generate
* the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one).
* When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
* the last step cherry picks the 2nd arg, we get a zero.
*/
#define __is_defined(x) ___is_defined(x)
#define ___is_defined(val) ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk) __take_second_arg(arg1_or_junk 1, 0)
Run Code Online (Sandbox Code Playgroud)
它是 C99,适用于三态选项(未定义,定义为 0,定义为 1)。
示例代码:
#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");
}
Run Code Online (Sandbox Code Playgroud)
TRACE_DO_根据宏定义的可能值适当调整测试。请注意,支持非数字值的定义可能会出现问题,因为很难将它们与宏名称区分开来......
将字符串化的宏(名称)与宏的字符串化(扩展)值进行比较:
#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)
Run Code Online (Sandbox Code Playgroud)
"" # macro扩展名将其扩展为字符串形式的宏名称,而TRACE_STRINGIFY(macro)首先扩展宏,然后将结果字符串化。如果两者不同,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;
}
Run Code Online (Sandbox Code Playgroud)
哪个输出
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
Run Code Online (Sandbox Code Playgroud)
换句话说,该TEST1符号未被识别为已定义(因为已对其进行了定义),也没有TEST11或TEST13没有括号。这些就是这种方法的局限性。
使用括号形式适用于所有无参数的宏(除了TEST1那些自己定义的宏)以及所有单参数宏。如果宏需要多个参数,则需要使用正确数量的逗号,否则(例如,如果您尝试过TRACE(TEST13(), "...")),则会出现编译时错误:“宏TEST13需要3个参数,仅提供1个参数”或类似的内容。
有什么问题吗
如果你可以将BLAalways定义为0or 1(或其他值可转换为bool),你可以这样做
#define TRACE(x, y) if (x) { std::cout << y << std::endl; }
Run Code Online (Sandbox Code Playgroud)
一个好的编译器会优化 中的常量表达式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; }
Run Code Online (Sandbox Code Playgroud)
FOO请注意,只有在未定义、或定义为空或定义为某个数字时,它才有效。
工作原理
对于类似函数的宏,扩展的工作方式如下:首先扩展所有宏参数,除非它们与#或一起使用##,然后扩展宏本身(请参阅16.3.1C++ 标准或6.10.3.1C 标准章节中的正式解释) )。
所以用扩展的宏来IS_DEFINED(x)“调用” 。如果定义了,它将被替换为它定义的内容,否则它将按原样传递。IS_DEFINED2(x)xx
如果你IS_DEFINED2(FOO)直接调用,那么#x将永远等于"FOO"这不是你想要的。