'gcc macro expansion of #error

I have some preprocessor code like this:

#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

#if GCC_VERSION < 40503
#error Your compiler is outdated.
#error You need at least gcc 4.5.3 for this library.
#error You have: GCC_VERSION    
#error You have: STR(GCC_VERSION)
#endif

However both ways I tried to output the current compiler version fail. According to the documentation this is because

Neither ‘#error’ nor ‘#warning’ macro-expands its argument. Internal whitespace sequences are each replaced with a single space. The line must consist of complete tokens. It is wisest to make the argument of these directives be a single string constant; this avoids problems with apostrophes and the like.

How can I output the compiler version with the error message anyway? Is there any preprocessor magic that allows me to achieve this?



Solution 1:[1]

Combining the proposal of paxdiablo and ideas for static compile time assertions I settled for the following solution.

#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)

#define ERROR_MESSAGE(major, minor, patchlevel) compiler_version__GCC_ ## major ## _ ## minor ## _ ## patchlevel ## __ ;
#define OUTDATED_COMPILER_ERROR(major, minor, patchlevel) ERROR_MESSAGE(major, minor, patchlevel)

#if GCC_VERSION < 40503
#error Outdated compiler version < 4.5.3
#error Absolute minimum recommended version is avr-gcc 4.5.3.
#error Use 'avr-gcc --version' from the command line to verify your compiler version.
#error Arduino 1.0.0 - 1.0.6 ship with outdated compilers.
#error Arduino 1.5.8 (avr-gcc 4.8.1) and above are recommended.

OUTDATED_COMPILER_ERROR(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#endif

Solution 2:[2]

The classic "solution" to this problem is to manage to generate an error which includes the desired message. For example:

#define S(x) #x
#define STR(x) S(x)
#if GCC_VERSION < 40503
  #error Outdated compiler: you need at least gcc 4.5.3 for this library.
  #define above_message_indicates_installed_gcc_version STR(Installed GCC version: GCC_VERSION)
  #include above_message_indicates_installed_gcc_version
#endif

While that does provide some information, it is still subject to misinterpretation; personally, I think letting the developer know the minimum version required is sufficient.

Note: The above snippet assumes that GCC_VERSION has already been defined, which I assume must have been done before the snippet in the OP. But beware: since the preprocessor does text substitution, not arithmetic evaluation (except in #if directives), you need to assemble GCC_VERSION in a different form for the error message If you want to try this, you could use the following:

#define GCC_VERSION (__GNUC_PATCHLEVEL__ + 100 * (__GNUC_MINOR__ + 100 * __GNUC__))

#define S_(x) #x
#define STR(x) S_(x)

#if GCC_VERSION < 40503
  #undef GCC_VERSION
  #define GCC_VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
  #error Outdated compiler: you need at least gcc 4.5.3 for this library.
  #define above_message_indicates_installed_gcc_version STR(Installed GCC version: GCC_VERSION)
  #include above_message_indicates_installed_gcc_version
#endif

See it on gcc.godbolt

Solution 3:[3]

If #error does not expand its arguments, I would just take a pragmatic approach:

#if GCC_VERSION < 40503
    #error Outdated compiler < 4.5.3, run 'gcc --version' to get version.
#endif

The only other possibility I can think of is to preprocess the file with your own preprocessor, to replace all occurences of (for example) xyzzy_GCC_VERSION_plugh with something extracted from gcc --version.

That's going to be a lot of effort and maintenance load to do what probably isn't necessary anyway.

But, if you really want to do it, you could use something like:

actual=$(echo "GCC_VERSION" | gcc -E - | tail -1)
sed "s/xyzzy_GCC_VERSION_plugh/$actual/g" file1.c >file1_morphed.c
gcc -c -o file1.o file1_morphed.c

and your file1.c would contain at the top somewhere:

#if GCC_VERSION < 40503
    #error Outdated compiler xyzzy_GCC_VERSION_plugh < 4.5.3
#endif

But, as i said, that's a fair bit of work for not much benefit. My advice is just to tell the person using your library how to get the compiler version themselves. They are developers after all, I'd expect them to be able to handle an instruction like that.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3