我必须在C中执行类似的操作。仅当使用char时才有效,但是我需要一个字符串。我怎样才能做到这一点?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
我必须在C中执行类似的操作。仅当使用char时才有效,但是我需要一个字符串。我怎样才能做到这一点?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
Answers:
我不认为有一种方法可以在预处理程序指令中完全进行可变长度字符串比较。您也许可以执行以下操作:
#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代码。
#define USER_VS (3 - USER)
在这种情况下。:)
[更新:2018.05.03]
CAVEAT:并非所有的编译器都以相同的方式实现C ++ 11规范。以下代码可在我测试过的编译器中使用,而许多注释者使用其他编译器。
引用Shafik Yaghmour的回答:在编译时计算C字符串的长度。这真的是constexpr吗?
不能保证在编译时对常量表达式进行求值,但是从C ++标准草案第5.19节常量表达式中我们只能得到一个非规范性的引用,尽管如此:
[...]> [注意:常量表达式可以在翻译过程中求值。
这个词can
改变了世界。
因此,constexpr
根据编译器作者对规范的解释,YMMV对此问题(或任何问题)的涉及。
[2016.01.31更新]
由于某些人不喜欢我之前的答案,因为它不需要字符串比较即可完成目标,从而避免了compile time string compare
OP的整个方面,因此这里提供了更详细的答案。
你不能!不在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
。
该字符串化宏S(...)
使用宏间接这样命名宏的值被转换成字符串。而不是宏的名称。
因此,USER##_VS
成为jack_VS
(或queen_VS
),这取决于你如何设置USER
。
稍后,当将stringify宏用作(在本示例中)S(USER_VS)
的值时,将传递到间接步骤,该步骤将其值()转换为string 。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以纠正错字。]
#if 0 == c_strcmp( USER, JACK )
为constexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
示例中,任何constexpr变量都等于零。您的示例仅由于USER是JACK而起作用。若用户是QUEEN,它会说USER IS QUEEN
和USER_VS IS QUEEN
constexpr
从预处理程序指令调用函数(甚至)。
以下对我有用。允许显示为符号宏值比较的内容。#error xxx只是看编译器的实际作用。用#define cat(a,b)a ## b替换cat定义会破坏事情。
#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)
第一行(macro P_()
)添加一个间接寻址,以使下一行(macro VS()
)在字符串化之前完成串联操作(请参阅 为什么我需要对宏进行双层间接寻址?)。字符串化宏(S()
和S_()
)来自Jesse。
该表(宏jack_VS
和queen_VS
)比Jesse的if-then-else构造容易维护。
最后,下一个四行代码块调用函数样式的宏。最后四行代码来自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
宏在字符串化之前没有展开。
#if (S(USER)=="jack")
-使用"
-时出现预处理器错误error: invalid token at start of a preprocessor expression
。
如果您的字符串是编译时间常数(如您的情况),则可以使用以下技巧:
#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
编译器可以提前告知strcmp的结果,并将其结果替换为strcmp,从而为您提供一个#define,可以将其与预处理器指令进行比较。我不知道编译器之间的差异/对编译器选项的依赖性,但这在GCC 4.7.2上对我有用。
编辑:经过进一步调查,它看起来像是一个工具链扩展,而不是GCC扩展,因此请考虑...
$
某种预处理程序扩展吗?
#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
代替 #define USER 'Q'
#define USER QUEEN
应该也可以,但是没有经过测试 也可以,并且可能更易于处理。
编辑:根据@Jean-FrançoisFabre的评论,我修改了我的答案。
(s==QUEEN?1:0)
由(s==QUEEN)
你不需要三元,结果已经是一个布尔
#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\
ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ;
#define ch0 'j'
#define ch1 'a'
#define ch2 'c'
#define ch3 'k'
#if USER_IS('j','a','c','k',0,0,0,0)
#define USER_VS "queen"
#elif USER_IS('q','u','e','e','n',0,0,0)
#define USER_VS "jack"
#endif
它基本上是手动初始化的固定长度的静态char数组,而不是自动初始化的可变长度的静态char数组,始终总是以终止的null char结尾
如果将USER定义为带引号的字符串,则无法执行此操作。
但是,如果USER只是JACK或QUEEN或Joker或其他任何东西,您都可以这样做。
有两个技巧可以使用:
#define JACK
任何事情因此,让我们开始:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
现在,如果我编写JACK_QUEEN_OTHER(USER)
,并且USER是JACK,则预处理器会将其转换为EXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
第二步是串联:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
现在JACK_QUEEN_OTHER(USER)
变成EXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
这使您有机会根据字符串是否匹配来添加多个逗号:
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
如果USER为JACK,则JACK_QUEEN_OTHER(USER)
变为EXPANSION2(x,x,x, 1, 2, 3)
如果USER是QUEEN,则JACK_QUEEN_OTHER(USER)
成为EXPANSION2(x,x, 1, 2, 3)
如果USER是其他用户,则JACK_QUEEN_OTHER(USER)
成为EXPANSION2(ReSeRvEd_other, 1, 2, 3)
此时,发生了一些关键事件:EXPANSION2宏的第四个参数是1、2或3,具体取决于传递的原始参数是jack,queen还是其他。因此,我们要做的就是挑选出来。出于复杂的原因,最后一步需要两个宏;它们将是EXPANSION2和EXPANSION3,即使其中的一个似乎不必要。
放在一起,我们有以下6个宏:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
#define EXPANSION2(a, b, c, d, ...) EXPANSION3(a, b, c, d)
#define EXPANSION3(a, b, c, d, ...) d
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
您可能会这样使用它们:
int main() {
#if JACK_QUEEN_OTHER(USER) == 1
printf("Hello, Jack!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 2
printf("Hello, Queen!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 3
printf("Hello, who are you?\n");
#endif
}
强制性Godbolt链接:https ://godbolt.org/z/8WGa19