我必须在C中做这样的事情。它只有在我使用char时才有效,但我需要一个字符串。我怎样才能做到这一点?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
我认为没有办法在预处理程序指令中完全进行可变长度字符串比较。您可以执行以下操作:
#define USER_JACK 1
#define USER_QUEEN 2
#define USER USER_JACK
#if USER == USER_JACK
#define USER_VS USER_QUEEN
#elif USER == USER_QUEEN
#define USER_VS USER_JACK
#endif
或者您可以稍微重构代码并使用C代码。
[更新:2018.05.03]
CAVEAT:并非所有编译器都以相同的方式实现C ++ 11规范。下面的代码在我测试的编译器中工作,而许多评论者使用不同的编译器。
引自Shafik Yaghmour的回答:Computing length of a C string at compile time. Is this really a constexpr?
在编译时不保证对常量表达式进行求值,我们只有草案C ++标准5.19节的非规范性引用,但这样说:
[...]> [注意:在翻译过程中可以评估常量表达式。-end note]
这个词can
在世界上有所不同。
所以,YMMV对这个(或任何)涉及constexpr
的答案,取决于编译器作者对规范的解释。
[更新2016.01.31]
有些人不喜欢我之前的回答,因为它通过完成目标而不需要字符串比较来避免OP的整个compile time string compare
方面,这里有一个更详细的答案。
你不能!不在C98或C99中。甚至没有在C11。没有多少MACRO操作会改变这一点。
在const-expression
中使用的#if
的定义不允许使用字符串。
它允许使用字符,因此如果您将自己限制为字符,则可以使用此字符:
#define JACK 'J'
#define QUEEN 'Q'
#define CHOICE JACK // or QUEEN, your choice
#if 'J' == CHOICE
#define USER "jack"
#define USER_VS "queen"
#elif 'Q' == CHOICE
#define USER "queen"
#define USER_VS "jack"
#else
#define USER "anonymous1"
#define USER_VS "anonymous2"
#endif
#pragma message "USER IS " USER
#pragma message "USER_VS IS " USER_VS
您可以!在C ++ 11中。如果为比较定义编译时辅助函数。
// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0
: (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0])
: c_strcmp( lhs+1, rhs+1 );
}
// some compilers may require ((int)lhs[0] - (int)rhs[0])
#define JACK "jack"
#define QUEEN "queen"
#define USER JACK // or QUEEN, your choice
#if 0 == c_strcmp( USER, JACK )
#define USER_VS QUEEN
#elif 0 == c_strcmp( USER, QUEEN )
#define USER_VS JACK
#else
#define USER_VS "unknown"
#endif
#pragma message "USER IS " USER
#pragma message "USER_VS IS " USER_VS
因此,最终,您将不得不改变实现为USER
和USER_VS
选择最终字符串值的目标的方式。
您不能在C99中进行编译时字符串比较,但您可以选择字符串进行编译时。
如果您真的必须进行编译时间比较,那么您需要更改为允许该功能的C ++ 11或更新版本。
[原始回答]
尝试:
#define jack_VS queen
#define queen_VS jack
#define USER jack // jack or queen, your choice
#define USER_VS USER##_VS // jack_VS or queen_VS
// stringify usage: S(USER) or S(USER_VS) when you need the string form.
#define S(U) S_(U)
#define S_(U) #U
更新:ANSI令牌粘贴有时不太明显。 ;-D
在宏之前放置一个#
会导致它被更改为其值的字符串,而不是其裸值。
在两个令牌之间放置一个双##
会使它们连接成一个令牌。
所以,宏USER_VS
有扩展jack_VS
或queen_VS
,取决于你如何设置USER
。
stringify宏S(...)
使用宏间接,因此命名宏的值将转换为字符串。而不是宏的名称。
因此,USER##_VS
变成jack_VS
(或queen_VS
),取决于你如何设置USER
。
之后,当stringify宏用作S(USER_VS)
时,USER_VS
(本例中为jack_VS
)的值被传递给间接步骤S_(jack_VS)
,后者将其值(queen
)转换为字符串"queen"
。
如果你将USER
设置为queen
,那么最终结果就是字符串"jack"
。
有关标记连接,请参阅:https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
有关令牌字符串转换,请参阅:https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[更新2015.02.15纠正错字。]
使用数值而不是字符串。
最后,要将常量JACK或QUEEN转换为字符串,请使用stringize(和/或tokenize)运算符。
以下是clang为我工作的。允许显示为符号宏值比较。 #error xxx只是为了看看编译器到底做了什么。用#define cat(a,b)替换cat定义a ## b会破坏事物。
#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__
#define xUSER_jack 0
#define xUSER_queen 1
#define USER_VAL cat(xUSER_,USER)
#define USER jack // jack or queen
#if USER_VAL==xUSER_jack
#error USER=jack
#define USER_VS "queen"
#elif USER_VAL==xUSER_queen
#error USER=queen
#define USER_VS "jack"
#endif
如上所述,ISO-C11预处理器不支持字符串比较。但是,分配具有“相反值”的宏的问题可以通过“令牌粘贴”和“表访问”来解决。 Jesse的简单连接/字符串化宏解决方案因gcc 5.4.0而失败,因为字符串化是在评估连接之前完成的(符合ISO C11)。但是,它可以修复:
#define P_(user) user ## _VS
#define VS(user) P_ (user)
#define S(U) S_(U)
#define S_(U) #U
#define jack_VS queen
#define queen_VS jack
S (VS (jack))
S (jack)
S (VS (queen))
S (queen)
#define USER jack // jack or queen, your choice
#define USER_VS USER##_VS // jack_VS or queen_VS
S (USER)
S (USER_VS)
第一行(宏P_()
)添加一个间接让下一行(宏VS()
)在字符串化之前完成连接(参见Why do I need double layer of indirection for macros?)。字符串化宏(S()
和S_()
)来自Jesse。
表格(宏jack_VS
和queen_VS
)比OP的if-then-else结构更容易维护,来自Jesse。
最后,下一个四行块调用函数式宏。最后四行是来自Jesse的回答。
在foo.c
中存储代码并调用预处理器gcc -nostdinc -E foo.c
会产生:
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"
"jack"
"USER_VS"
输出符合预期。最后一行显示USER_VS
宏在字符串化之前未展开。
qazxsw poi和qazxsw poi的回答让我做了以下事情:
Patrick
而不是Jesse Chisholm #define QUEEN 'Q'
#define JACK 'J'
#define CHECK_QUEEN(s) (s==QUEEN)
#define CHECK_JACK(s) (s==JACK)
#define USER 'Q'
[... later on in code ...]
#if CHECK_QUEEN(USER)
compile_queen_func();
#elif CHECK_JACK(USER)
compile_jack_func();
#elif
#error "unknown user"
#endif
也应该工作但没有经过测试
也工作,可能更容易处理。
编辑:根据@ Jean-FrançoisFabre的评论我调整了我的答案。
如果你的字符串是编译时常量(如你的情况),你可以使用以下技巧:
#define USER 'Q'
编译器可以提前告诉strcmp的结果,并将strcmp替换为其结果,从而为您提供一个可以与预处理器指令进行比较的#define。我不知道编译器/依赖编译器选项之间是否存在任何差异,但它对GCC 4.7.2有用。
编辑:经过进一步调查,看起来这是一个工具链扩展,而不是GCC扩展,所以考虑到这一点......
#define USER QUEEN
它基本上是手动初始化的固定长度静态字符数组,而不是自动初始化的可变长度静态字符数组,总是以终止空字符结尾
这很简单,我想你可以说
#define USER_JACK strcmp(USER, "jack")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_JACK == 0
#define USER_VS USER_QUEEN
#elif USER_QUEEN == 0
#define USER_VS USER_JACK
#endif