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"开发者_StackOverflow社区
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
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.
[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.]
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
Use numeric values instead of strings.
Finally to convert the constants JACK or QUEEN to a string, use the stringize (and/or tokenize) operators.
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.
I know technically this isn't answering the OP's question, but in looking at the answers above, I am realizing (from what I can understand) that there isn't any easy way to do string comparison in the preprocessor without resorting to some "tricks" or other compiler specific magic. So in rethinking it for my situation, I realized that in reality there would only be a fixed set of strings that you would want to/could compare against, as the preprocessor would have to use static strings anyway. So it is more of a stylistic thing to be able to compare against a "string" like thing in your code. So I decided to add definitions that had the syntax like a string (when reading it) but were just defines for integers, which is what it looks like some other people have suggested. For example:
#if USER == USER_JACK
// do something
#elif USER == USER_QUEEN
// do something else
#elif USER == USER_KING
// do something completely different
#else
// abort abort
#end
So now it is just a question of setting up the definitions appropriately.
As a more concrete example, I originally wanted to do the string comparison so I could specify a default archive type when using the Cereal serialization library. In Cereal there are 3 valid Archive types: JSON, XML, and Binary, and I wanted the user to be able to input those as a string variable within CMake. I still make that possible (and also constrain the variables using CMake's CACHE STRINGS Property), but then convert the string to an integer before passing it as a compiler definition. (My apologies in advance as I know this is CMake-centric and that that wasn't part of the original question.)
Using CMake to automate things, in the CMakeLists.txt file, I include the following SetupCereal.cmake script:
set( CEREAL_DIR "" CACHE PATH "Path to Cereal installation" )
set( CEREAL_INCLUDE_DIR ${CEREAL_DIR}/include )
# Set up the user input variable and constrain to valid values
set( CEREAL_ARCHIVE_DEFAULT_TYPE "JSON"
CACHE STRING
"Default Archive type to use for Cereal serialization"
)
set_property( CACHE CEREAL_ARCHIVE_DEFAULT_TYPE
PROPERTY STRINGS JSON XML BINARY
)
# Convert the string to integer for preprocessor comparison
if ( "${CEREAL_ARCHIVE_DEFAULT_TYPE}" STREQUAL "JSON")
set( CEREAL_ARCHIVE_DEFAULT_TYPE_VALUE 0 )
elseif( "${CEREAL_ARCHIVE_DEFAULT_TYPE}" STREQUAL "XML" )
set( CEREAL_ARCHIVE_DEFAULT_TYPE_VALUE 1 )
elseif( "${CEREAL_ARCHIVE_DEFAULT_TYPE}" STREQUAL "BINARY" )
set( CEREAL_ARCHIVE_DEFAULT_TYPE_VALUE 2 )
endif()
# Setup the corresponding preprocessor definitions
set( CEREAL_DEFINES
-DCEREAL_ARCHIVE_JSON=0
-DCEREAL_ARCHIVE_XML=1
-DCEREAL_ARCHIVE_BINARY=2
-DCEREAL_ARCHIVE_DEFAULT_TYPE=${CEREAL_ARCHIVE_DEFAULT_TYPE_VALUE}
)
I then made an accompanying CerealArchive.hpp header which looks like:
#pragma once
#if CEREAL_ARCHIVE_DEFAULT_TYPE == CEREAL_ARCHIVE_JSON
# include <cereal/archives/json.hpp>
namespace cereal
{
using DefaultOutputArchive = JSONOutputArchive;
using DefaultInputArchive = JSONInputArchive;
}
#elif CEREAL_ARCHIVE_DEFAULT_TYPE == CEREAL_ARCHIVE_XML
# include <cereal/archives/xml.hpp>
namespace cereal {
using DefaultOutputArchive = XMLOutputArchive;
using DefaultInputArchive = XMLInputArchive;
} // namespace cereal
#elif CEREAL_ARCHIVE_DEFAULT_TYPE == CEREAL_ARCHIVE_BINARY
# include <cereal/archives/binary.hpp>
namespace cereal
{
using DefaultOutputArchive = BinaryOutputArchive;
using DefaultInputArchive = BinaryInputArchive;
}
#endif // CEREAL_ARCHIVE_DEFAULT_TYPE
And then the client code looks like:
#include <CerealArchive.hpp>
#include <sstream>
std::ostringstream oss;
{
cereal::DefaultOutputArchive archive( oss );
archive( 123 );
}
std::string s = oss.str();
The default Archive type can then be selected by the developer as a CMake string variable (followed of course by a recompile).
So while technically this solution isn't comparing strings, syntactically it kind of behaves/looks the same.
I'm also thinking that the the SetupCereal.cmake could be further generalized to encapsulate the setup in a function, so it could be used in other situations where you want to define similar types of definitions.
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...
You can't do that if USER is defined as a quoted string.
But you can do that if USER is just JACK or QUEEN or Joker or whatever.
There are two tricks to use:
- Token-splicing, where you combine an identifier with another identifier by just concatenating their characters. This allows you to compare against JACK without having to
#define JACK
to something - variadic macro expansion, which allows you to handle macros with variable numbers of arguments. This allows you to expand specific identifiers into varying numbers of commas, which will become your string comparison.
So let's start out with:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
Now, if I write JACK_QUEEN_OTHER(USER)
, and USER is JACK, the preprocessor
turns that into EXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
Step two is concatenation:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
Now JACK_QUEEN_OTHER(USER)
becomes EXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
This gives the opportunity to add a number of commas according to whether or not a string matches:
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
If USER is JACK, JACK_QUEEN_OTHER(USER)
becomes EXPANSION2(x,x,x, 1, 2, 3)
If USER is QUEEN, JACK_QUEEN_OTHER(USER)
becomes EXPANSION2(x,x, 1, 2, 3)
If USER is other, JACK_QUEEN_OTHER(USER)
becomes EXPANSION2(ReSeRvEd_other, 1, 2, 3)
At this point, something critical has happened: the fourth argument to the EXPANSION2 macro is either 1, 2, or 3, depending on whether the original argument passed was jack, queen, or anything else. So all we have to do is pick it out. For long-winded reasons, we'll need two macros for the last step; they'll be EXPANSION2 and EXPANSION3, even though one seems unnecessary.
Putting it all together, we have these 6 macros:
#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
And you might use them like this:
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
}
Obligatory godbolt link: https://godbolt.org/z/8WGa19
MSVC Update: You have to parenthesize slightly differently to make things also work in MSVC. The EXPANSION* macros look like this:
#define EXPANSION1(a, b, c, d, e) EXPANSION2((a##b, c, d, e))
#define EXPANSION2(x) EXPANSION3 x
#define EXPANSION3(a, b, c, d, ...) d
Obligatory: https://godbolt.org/z/96Y8a1
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.
While the pre-processor is very limited with respect to strings, most compilers know a lot about strings at compile-time too. For instance this can successfully compare __BASE_FILE__
and __FILE__
at compile-time:
const int zero_div_warning __attribute__((unused)) =
42 / !strcmp(__FILE__ , __BASE_FILE__);
Compilation of this with gcc -Wno-div-by-zero -Werr
succeeds when found in a .c file and fails when found in a .h file (static inline function)
-Wno-div-by-zero is part of -Wall
While this may not solve your particular use case, it does open many possibilities to compare constant strings at compile-time.
#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
I've wanted to use string comparison as well in preprocessor macros, mostly so I can also "print" these values during preprocessing step (using pragma message).
As mentioned in accepted answer:
I don't think there is a way to do variable length string comparisons completely in preprocessor directives.
... however it seems doable, if the string value is written as an array of characters, both for the "variable"/tested macro, and for the macros that represent values to be tested against. For instance, I have tested the following code in https://replit.com/languages/c :
#include <stdio.h>
#define XSTR(x) STR(x)
#define STR(x) #x
#define TEST_STR ('t','e','s','t')
#define OTHER_STR ('o','t','h','e','r')
#define MYMACRO ('t','e','s','t')
#pragma message( "MYMACRO is: " XSTR(MYMACRO) )
#if MYMACRO==TEST_STR
#pragma message( "IS TRUE" )
#else
#pragma message( "IS FALSE" )
#endif
int main() {
printf("Hello, world!\r\n");
return 0;
}
... and it outputs:
> clang-7 -pthread -lm -o main main.c
main.c:10:9: warning: MYMACRO is: ('t','e','s','t')
[-W#pragma-messages]
#pragma message( "MYMACRO is: " XSTR(MYMACRO) )
^
main.c:12:9: warning: IS TRUE [-W#pragma-messages]
#pragma message( "IS TRUE" )
^
2 warnings generated.
> ./main
Hello, world!
>
... and if I change the condition to:
#if MYMACRO==OTHER_STR
... and recompile, the output is:
> clang-7 -pthread -lm -o main main.c
main.c:10:9: warning: MYMACRO is: ('t','e','s','t')
[-W#pragma-messages]
#pragma message( "MYMACRO is: " XSTR(MYMACRO) )
^
main.c:14:9: warning: IS FALSE [-W#pragma-messages]
#pragma message( "IS FALSE" )
^
2 warnings generated.
> ./main
Hello, world!
>
So, if the tested macro and the value macros are defined as lists of character values, then they can be compared directly in a preprocessor #if
conditional - and in order to print such macros in a pragma message, use of stringifying macros are required.
Note that the commas and single quotation marks will remain visible, when printing macros like these in pragma message (I guess it might be possible to use variadic macros/__VA_ARGS__
to make a macro, that would loop through the character array values and concatenate them in a more readable manner, but the above technique is good enough for me).
#define USER "jack" // jack or queen
#ifdef USER \
if (USER == "jack")#define USER_VS "queen" \
else if(USER == "queen") #define USER_VS "jack"
#endif
It's simple I think you can just say
#define NAME JACK
#if NAME == queen
精彩评论