问题
I have to do something like this in C. It works only if I use a char, but I need a string. How can I do this?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
回答1:
I don't think there is a way to do variable length string comparisons completely in preprocessor directives. You could perhaps do the following though:
#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
Or you could refactor the code a little and use C code instead.
回答2:
[UPDATE: 2018.05.03]
CAVEAT: Not all compilers implement the C++11 specification in the same way. The below code works in the compiler I tested on, while many commenters used a different compiler.
Quoting from Shafik Yaghmour's answer at: Computing length of a C string at compile time. Is this really a constexpr?
Constant expressions are not guaranteed to be evaluated at compile time, we only have a non-normative quote from draft C++ standard section 5.19 Constant expressions that says this though:
[...]>[ Note: Constant expressions can be evaluated during translation.—end note ]
That word can
makes all the difference in the world.
So, YMMV on this (or any) answer involving constexpr
, depending on the compiler writer's interpretation of the spec.
[UPDATED 2016.01.31]
As some didn't like my earlier answer because it avoided the whole compile time string compare
aspect of the OP by accomplishing the goal with no need for string compares, here is a more detailed answer.
You can't! Not in C98 or C99. Not even in C11. No amount of MACRO manipulation will change this.
The definition of const-expression
used in the #if
does not allow strings.
It does allow characters, so if you limit yourself to characters you might use this:
#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
You can! In C++11. If you define a compile time helper function for the comparison.
// 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
So, ultimately, you will have to change the way you accomlish your goal of choosing final string values for USER
and USER_VS
.
You can't do compile time string compares in C99, but you can do compile time choosing of strings.
If you really must do compile time sting comparisons, then you need to change to C++11 or newer variants that allow that feature.
[ORIGINAL ANSWER FOLLOWS]
Try:
#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
UPDATE: ANSI token pasting is sometimes less than obvious. ;-D
Putting a single #
before a macro causes it to be changed into a string of its value, instead of its bare value.
Putting a double ##
between two tokens causes them to be concatenated into a single token.
So, the macro USER_VS
has the expansion jack_VS
or queen_VS
, depending on how you set USER
.
The stringify macro S(...)
uses macro indirection so the value of the named macro gets converted into a string. instead of the name of the macro.
Thus USER##_VS
becomes jack_VS
(or queen_VS
), depending on how you set USER
.
Later, when the stringify macro is used as S(USER_VS)
the value of USER_VS
(jack_VS
in this example) is passed to the indirection step S_(jack_VS)
which converts its value (queen
) into a string "queen"
.
If you set USER
to queen
then the final result is the string "jack"
.
For token concatenation, see: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
For token string conversion, see: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[UPDATED 2015.02.15 to correct a typo.]
回答3:
Use numeric values instead of strings.
Finally to convert the constants JACK or QUEEN to a string, use the stringize (and/or tokenize) operators.
回答4:
The following worked for me with clang. Allows what appears as symbolic macro value comparison. #error xxx is just to see what compiler really does. Replacing cat definition with #define cat(a,b) a ## b breaks things.
#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
回答5:
As already stated above, the ISO-C11 preprocessor does not support string comparison. However, the problem of assigning a macro with the “opposite value” can be solved with “token pasting” and “table access”. Jesse’s simple concatenate/stringify macro-solution fails with gcc 5.4.0 because the stringization is done before the evaluation of the concatenation (conforming to ISO C11). However, it can be fixed:
#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)
The first line (macro P_()
) adds one indirection to let the next line (macro VS()
) finish the concatenation before the stringization (see Why do I need double layer of indirection for macros?). The stringization macros (S()
and S_()
) are from Jesse.
The table (macros jack_VS
and queen_VS
) which is much easier to maintain than the if-then-else construction of the OP is from Jesse.
Finally, the next four-line block invokes the function-style macros. The last four-line block is from Jesse’s answer.
Storing the code in foo.c
and invoking the preprocessor gcc -nostdinc -E foo.c
yields:
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"
"jack"
"USER_VS"
The output is as expected. The last line shows that the USER_VS
macro is not expanded before stringization.
回答6:
The answere by Patrick and by Jesse Chisholm made me do the following:
#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
Instead of #define USER 'Q'
#define USER QUEEN
should also work but was not tested also works and might be easier to handle.
EDIT: According to the comment of @Jean-François Fabre I adapted my answer.
回答7:
If your strings are compile time constants (as in your case) you can use the following trick:
#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
The compiler can tell the result of the strcmp in advance and will replace the strcmp with its result, thus giving you a #define that can be compared with preprocessor directives. I don't know if there's any variance between compilers/dependance on compiler options, but it worked for me on GCC 4.7.2.
EDIT: upon further investigation, it look like this is a toolchain extension, not GCC extension, so take that into consideration...
回答8:
#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
it basically a fixed length static char array initialized manually instead of a variable length static char array initialized automatically always ending with a terminating null char
回答9:
It's simple I think you can just say
#define NAME JACK
#if NAME == queen
来源:https://stackoverflow.com/questions/2335888/how-to-compare-strings-in-c-conditional-preprocessor-directives