How to compare strings in C conditional preprocessor-directives

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

Solution 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.

Solution 2:

[UPDATE: 2021.01.04]

One thing that has changed since I first posted this in 2014, is the format of #pragma message.

Nowadays, the parens are required!

#pragma message ("USER    IS " USER)
#pragma message ("USER_VS IS " USER_VS)

That said, the 2016 code (using characters, not strings) still works in VS2019.

But, as @Artyer points out, the version involving c_strcmp will NOT work in ANY modern compiler.

[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.

[2021.01.04: CAVEAT: This does not work in any MODERN compiler. See comment by @Artyer.]

// 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.]

Solution 3:

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

Solution 4:

Use numeric values instead of strings.

Finally to convert the constants JACK or QUEEN to a string, use the stringize (and/or tokenize) operators.

Solution 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.