Reputation:
I am posting yet another programming task that I can't solve. Suppose we write OS-specific preprocessor directives like:
#if OS == Linux
/* Linux-specific instructions */
...
#else
#if OS == Darwin
/* Mac OSX-specific instructions */
...
#else
/* Program code for other OS */
...
#endif
#endif
We are not allowed to use #define OS Linux;
instead the variable OS
should be set with the compiler (or alternatively in a makefile). There should also be a shell-command which can automatically define OS
. How can we set OS
-Variable using the compiler or the shell?
Kind regards
Upvotes: 1
Views: 346
Reputation: 107809
The C preprocessor can only compare integer values, not strings or any other type. When you write #if OS == Linux
, the preprocessor first expands the macros OS
and Linux
(and any macros in their expansions, etc.). Once it's finished expanding, it treats the whole line (after #if
itself) as a preprocessor expression and evaluates it. Like with the C language proper, the directive is true if the value is nonzero, and boolean operators like ==
evaluate to 1 if true and 0 if false.
So for #if OS == Linux
to work, you have to choose a numerical value for Linux
(and a different value for all the other operating systems). Then you need to assign OS
to the same value. For example, if suppose you have a header operating_systems.h
containing
#define Linux 1
#define Darwin 2
#define Windows 3
then you can use #define OS 1
or #define OS (7 - 6)
or #define OS Linux
or any equivalent method to make #if OS == Linux
true.
Note that if Linux
and Darwin
are undefined and you define OS
to either Linux
or Darwin
, then both OS == Linux
and OS == Darwin
will be true, because undefined macro names in preprocessor expressions have the value 0. This kind of “enum-like” conditional is error-prone since undefined values (or values from the wrong “enum” series) will not be caught, so it isn't a good idea.
This is quite an unusual way to select between operating systems. Usually, system selection is based on whether certain macros are defined or not. For example, all compilers for Linux should define __linux__
, all compilers for macOS should define __APPLE__
, etc. To find out what macros are predefined, consult the documentation of your compiler or operating system (including applicable standards, e.g. POSIX), or ask your compiler (e.g. cpp -dM /dev/null
).
So typical OS-specific code looks like this:
#if defined(__APPLE__)
// macOS or iOS code
#elif defined(__unix__)
// code for Unix-like systems
# if defined(__linux__)
// additional code for Linux
# endif
#elif defined(_WIN32) || defined(_WIN64)
// code for Windows
#else
#error "Unknown operating system"
#endif
If you need to select additional information during the build which can't be determined by the compiler alone, most compilers have a way to predefine additional macros. On Unix-like compilers (including GCC, Clang and many others), you do this by passing -DMACRO_NAME
or -DMACRO_NAME=expansion
on the command line. For example, if you compile with
gcc -DPROTOCOL_VERSION=3
then you can write code like this:
#if PROTOCOL_VERSION == 1
#error "Protocol version 1 is no longer supported."
#elif PROTOCOL_VERSION == 2
// code for version 2
#elif PROTOCOL_VERSION == 3
// code for version 3
#else
#error "An unsupported protocol version was requrested"
#endif
(This example assumes your code is only capable of supporting a single protocol version.)
Upvotes: 2