1

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?

Udo Klein
  • 6,784
  • 1
  • 36
  • 61

3 Answers3

3

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

rici
  • 234,347
  • 28
  • 237
  • 341
  • Unfortunately this doesn't work -- it just gives an error message like "fatal error: above_message_indicates_installed_gcc_version: No such file or directory" with a direct copy of the text from the source file (macro name, unexpanded) – Chris Dodd Apr 22 '22 at 23:07
  • @ChrisDodd: I guess I should have made it more obvious that `GCC_VERSION` needs to be defined for this to work, both for the test and for the error message. I didn't bother with that when I wrote this lo! these many years ago, presumably because OP must have had some way to define it. See https://gcc.godbolt.org/z/Wev7z9b8P – rici Apr 23 '22 at 01:31
  • Interesting -- it works on ancient versions of gcc, but on anything more recent, the error message for the include shows the unexpanded macro, – Chris Dodd Apr 23 '22 at 05:24
  • @ChrisDodd: that seems wrong to me. A file not found error should let you know *which* file wasn't found; it's just simple courtesy. Maybe my macros don't work with modern gcc. I'll play around with it a bit more when I get some time. But with modern gcc, the question is moot, since you can just use `_Pragma`, which does allow macro substitution, albeit with the nuisance of double stringification. Maybe the answer needs an upgrade; it's almost as ancient as the versions of gcc with which it works. – rici Apr 23 '22 at 08:21
  • It does seem wrong, but I was never able to make it work with include (where it would show me the expansion of the macro I was trying to check). I ended up using `#pragma message` instead, which is gcc-specific, but does expand the macro. – Chris Dodd Apr 23 '22 at 18:52
  • @chris: I just checked my gcc.godbolt source with a variety of GCC versions up to gcc 9, and all of them presented the error message in some form or another, with the detected GCC version. It doesn't work correctly with clang, because all clang versions show as GCC 4.2.1 for some reason, but it does show the expanded macro anyway. – rici Apr 23 '22 at 19:00
  • if you have an example where it fails, please share – rici Apr 23 '22 at 19:01
2

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    Actually the long version of my macro already did this. However this library is targeted at Arduino users, so I would prefer to be as explicit as possible. – Udo Klein Dec 24 '14 at 11:46
  • @Udo, they're still developers though, yes? I assume so since they're using your library which can only be to link their own code with. All I'm saying that, if life gives you lemons, throw them away and go get a beer :-) In other words, since `#error` does _not_ expand, you have to do it yourself if that's what you want. Still, wait around, someone may have a better solution that my two suggestions. – paxdiablo Dec 24 '14 at 11:53
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
Community
  • 1
  • 1
Udo Klein
  • 6,784
  • 1
  • 36
  • 61