190

I am trying to figure out what version of Boost my code thinks it's using. I want to do something like this:

#error BOOST_VERSION

but the preprocessor does not expand BOOST_VERSION.

I know I could print it out at run-time from the program, and I know I could look at the output of the preprocessor to find the answer. I feel like having a way of doing this during compilation could be useful.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jim Hunziker
  • 14,111
  • 8
  • 58
  • 64
  • 10
    For future visitors... Chris Barry provides the generalized solution at the end (devoid of Boost specific stuff). – jww Sep 04 '15 at 01:41
  • For Visual C++ users, also check Bojan Resnik's answer below for `#pragma message` syntax – Scott Smith Dec 04 '20 at 19:39

14 Answers14

207

I know that this is a long time after the original query, but this may still be useful.

This can be done in GCC using the stringify operator "#", but it requires two additional stages to be defined first.

#define XSTR(x) STR(x)
#define STR(x) #x

The value of a macro can then be displayed with:

#pragma message "The value of ABC: " XSTR(ABC)

See: 3.4 Stringification in the gcc online documentation.

How it works:

The preprocessor understands quoted strings and handles them differently from normal text. String concatenation is an example of this special treatment. The message pragma requires an argument that is a quoted string. When there is more than one component to the argument then they must all be strings so that string concatenation can be applied. The preprocessor can never assume that an unquoted string should be treated as if it were quoted. If it did then:

#define ABC 123
int n = ABC;

would not compile.

Now consider:

#define ABC abc
#pragma message "The value of ABC is: " ABC

which is equivalent to

#pragma message "The value of ABC is: " abc

This causes a preprocessor warning because abc (unquoted) cannot be concatenated with the preceding string.

Now consider the preprocessor stringize (Which was once called stringification, the links in the documentation have been changed to reflect the revised terminology. (Both terms, incidentally, are equally detestable. The correct term is, of course, stringifaction. Be ready to update your links.)) operator. This acts only on the arguments of a macro and replaces the unexpanded argument with the argument enclosed in double quotes. Thus:

#define STR(x) #x
char *s1 = "abc";
char *s2 = STR(abc);

will assign identical values to s1 and s2. If you run gcc -E you can see this in the output. Perhaps STR would be better named something like ENQUOTE.

This solves the problem of putting quotes around an unquoted item, the problem now is that, if the argument is a macro, the macro will not be expanded. This is why the second macro is needed. XSTR expands its argument, then calls STR to put the expanded value into quotes.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Chris Barry
  • 2,250
  • 1
  • 14
  • 8
  • 10
    I'm curious as to why it requires two stages – Vincent Fourmond Jan 22 '15 at 13:34
  • 8
    @VincentFourmond Without the XSTR stage, the macro isn't expanded. So if you did #define ABC 42 \n STR(ABC) you'd get "ABC". See https://gcc.gnu.org/onlinedocs/cpp/Stringification.html – rob05c Feb 12 '15 at 22:22
  • This also works great with Xcode 8, e.g. replacing ABC with `__IPHONE_9_3`. – funroll Oct 11 '16 at 19:50
  • GCC terminology seems to have changed, and with it the URL, which is now [https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing](https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing) – Chris Barry Aug 29 '19 at 11:14
  • Didn't work for me, the message that I see is "The value of ABC: XSTR(ABC)" – Maxim May 14 '21 at 19:12
  • @Maxim: You cannot put the XSTR(ABC) inside the quotes. It has to be outside of the quotes. – DrP3pp3r Aug 18 '21 at 13:24
  • It doesn't work for me on gcc 9 with #pragma message "TEST " XSTR(PF_RING_VERSION) – teoring Aug 26 '21 at 13:56
  • What threw me initially is that #pragma message line got printed out verbatim (as in the source code), however, looking a line above this you will see `note:` prefixed to the evaluated string. – tonylo Sep 22 '21 at 23:47
  • Is there any way to remove the `note: in expansion of macro 'XSTR'` / `in definition of macro 'STR'` lines? – Dmitry Grigoryev Oct 04 '21 at 09:48
  • 2
    I was having trouble getting this to work with gcc 9.3 when the macro was defined as `#define METHODDEF static type`. What did work was to add `#define METHODDEF` causing a 'redefined' error that showed the actual expansion and where it was set, which was kinda nice. – erco Oct 22 '21 at 17:44
  • It doesn't do the math for me, just prints out the whole expression: `The value of ABC: (((16/8 * 96 * 2 / 2*8)/4) + 16/8 * 2)` Is there a way to print `The value of ABC: 388`? – endolith Jun 09 '22 at 16:51
  • 1
    Too bad this does not work with clang on macos. – oarfish Nov 22 '22 at 18:54
131

BOOST_PP_STRINGIZE seems a excellent solution for C++, but not for regular C.

Here is my solution for GNU CPP:

/* Some test definition here */
#define DEFINED_BUT_NO_VALUE
#define DEFINED_INT 3
#define DEFINED_STR "ABC"

/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "="  VALUE(var)

/* Some example here */
#pragma message(VAR_NAME_VALUE(NOT_DEFINED))
#pragma message(VAR_NAME_VALUE(DEFINED_BUT_NO_VALUE))
#pragma message(VAR_NAME_VALUE(DEFINED_INT))
#pragma message(VAR_NAME_VALUE(DEFINED_STR))

Above definitions result in:

test.c:10:9: note: #pragma message: NOT_DEFINED=NOT_DEFINED
test.c:11:9: note: #pragma message: DEFINED_BUT_NO_VALUE=
test.c:12:9: note: #pragma message: DEFINED_INT=3
test.c:13:9: note: #pragma message: DEFINED_STR="ABC"

For "defined as interger", "defined as string", and "defined but no value" variables , they work just fine. Only for "not defined" variable, they displayed exactly the same as original variable name. You have to used to it -- or maybe someone can provide a better solution.

Jackie Yeh
  • 1,567
  • 1
  • 9
  • 11
  • excellent! Any experiences in ARM RVCT? it seems has no "Stringification" feature as GCC http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/CIHHDEAA.html – xdan May 28 '13 at 07:07
  • 2
    Great solution. However, if I wish to display the size of a compile-time calculated value, e.g. the size of a complex struct, can this be done? The method suggested in this answer seems to generate `DEFINED_INT=(sizeof(MY_STRUCT))`, without the `sizeof` operator being evaluated. – Carl Mar 16 '15 at 12:12
  • (Comment addition: not unexpected, since it is the compiler rather than the pre-processor that will evaluate `sizeof`, however, still curious if there's a clever way of achieving this.) – Carl Mar 16 '15 at 12:21
  • @xdan Good solution, unfortunately is doesn't cater for things like `#define masks {0xff, 0xaf, 0x0f}` – Simon Bagley Aug 20 '19 at 10:35
61

If you are using Visual C++, you can use #pragma message:

#include <boost/preprocessor/stringize.hpp>
#pragma message("BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION))

Edit: Thanks to LB for link

Apparently, the GCC equivalent is (not tested):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)
Bojan Resnik
  • 7,320
  • 28
  • 29
  • 6
    That's called diagnostic pragmas, http://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas – LB40 Oct 20 '09 at 15:05
  • 4
    Would be nice if you included [the definition of `BOOST_PP_STRINGIZE`](http://www.boost.org/doc/libs/1_63_0/boost/preprocessor/stringize.hpp) which is nice and short and copy/pasteable. – Timmmm Feb 03 '17 at 09:46
  • Works fine under gcc :) – Thomas Legris Apr 05 '17 at 09:10
15

As far as I know '#error' only will print strings, in fact you don't even need to use quotes.

Have you tried writing various purposefully incorrect code using "BOOST_VERSION"? Perhaps something like "blah[BOOST_VERSION] = foo;" will tell you something like "string literal 1.2.1 cannot be used as an array address". It won't be a pretty error message, but at least it'll show you the relevant value. You can play around until you find a compile error that does tell you the value.

Dan Olson
  • 22,849
  • 4
  • 42
  • 56
KeyserSoze
  • 2,501
  • 1
  • 16
  • 17
15

Without boost :

  1. define same macro again and compiler HIMSELF will give warning.

  2. From warning you can see location of the previous definition.

  3. vi file of previous definition .

ambarish@axiom:~/cpp$ g++ shiftOper.cpp
shiftOper.cpp:7:1: warning: "LINUX_VERSION_CODE" redefined
shiftOper.cpp:6:1: warning: this is the location of the previous definition

#define LINUX_VERSION_CODE 265216
#define LINUX_VERSION_CODE 666

int main ()
{

}
Skandix
  • 1,916
  • 6
  • 27
  • 36
13

In Microsoft C/C++, you can use the built-in _CRT_STRINGIZE() to print constants. Many of my stdafx.h files contain some combination of these:

#pragma message("_MSC_VER      is " _CRT_STRINGIZE(_MSC_VER))
#pragma message("_MFC_VER      is " _CRT_STRINGIZE(_MFC_VER))
#pragma message("_ATL_VER      is " _CRT_STRINGIZE(_ATL_VER))
#pragma message("WINVER        is " _CRT_STRINGIZE(WINVER))
#pragma message("_WIN32_WINNT  is " _CRT_STRINGIZE(_WIN32_WINNT))
#pragma message("_WIN32_IE     is " _CRT_STRINGIZE(_WIN32_IE))
#pragma message("NTDDI_VERSION is " _CRT_STRINGIZE(NTDDI_VERSION)) 

and outputs something like this:

_MSC_VER      is 1915
_MFC_VER      is 0x0E00
_ATL_VER      is 0x0E00
WINVER        is 0x0600
_WIN32_WINNT  is 0x0600
_WIN32_IE     is 0x0700
NTDDI_VERSION is 0x06000000
UweBaemayr
  • 1,861
  • 1
  • 18
  • 21
6
#define a <::BOOST_VERSION>
#include a
MSVC2015: fatal error C1083: Cannot open include file: '::106200': No such file or directory

Pros:

  • Works on builtin macroses
  • Works even if preprocess to file is enabled, even if invalid tokens are present:
#define a <::'*/`#>
#include a
MSVC2015: fatal error C1083: Cannot open include file: '::'*/`#': No such file or directory
GCC4.x: warning: missing terminating ' character [-Winvalid-pp-token]
#define a <::'*/`#>

Cons:

  • Sometime fails because of invalid characters in the include file path. Can be fixed by change a prefix (see update section below).

Update:

For GCC 4.7.x and lower the output throws the error:

error: #include expects "FILENAME" or <FILENAME>

To fix that you can change the prefix:

#define a <.__cplusplus>
#include a
fatal error: .201103L: No such file or directory
Andry
  • 2,273
  • 29
  • 28
  • Mine just says `Build error: #include expects "FILENAME" or `. Sigh. – endolith Nov 09 '18 at 21:02
  • @endolith what compiler and version? – Andry Nov 12 '18 at 02:19
  • DP8051 Keil 9.51 :) – endolith Nov 12 '18 at 04:27
  • @endolith Seems this compiler is very limited on preprocessing: http://www.keil.com/support/man/docs/c51/c51_pp_directives.htm But, on mine side it almost works as expected, i've just removed some of invalid characters like `'`: `*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/\`'` – Andry Nov 12 '18 at 10:25
  • Thank you, this saved me because the pragma message stuff wasn't implemented in the compiler I was using. – CodeMonkey May 17 '19 at 08:08
3

You could also preprocess the source file and see what the preprocessor value evaluates to.

fbrereto
  • 35,429
  • 19
  • 126
  • 178
2

You could write a program that prints out BOOST_VERSION and compile and run it as part of your build system. Otherwise, I think you're out of luck.

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • For the case of a software version defined in a header you're probably safe (and it's a good answer). But as a general solution, a possible downside would be in getting your test app and your real app to have the same value of the #define - depending on their include paths, other #defines that may be used to set the value of that one, the CFLAGS passed to the compiler, etc. – KeyserSoze Oct 13 '09 at 18:57
  • Print it out from your real program. If graphical, put it in the "about" dialog. If command-line, make it an option (part of --version, maybe). If a daemon, write it to a log file. If embedded, find some other way. – divegeek Oct 20 '09 at 15:29
  • @swillden - The OP wanted it at compile time, not at runtime. – Chris Lutz Oct 20 '09 at 22:26
  • This also tends to break cross-compiler based builds – Craig Ringer Dec 21 '15 at 03:24
  • This is what I'm ending up doing, even though I'm cross-compiling for the AVR CPUs from LINUX. All the preprocessor calculations are in the header file, it it's easy enough to write a short program to test the values. I might use the accepted solution as well but both are better than trying to read an object dump file. – Deepstop Dec 20 '20 at 21:03
2

Are you looking for

#if BOOST_VERSION != "1.2"
#error "Bad version"
#endif

Not great if BOOST_VERSION is a string, like I've assumed, but there may also be individual integers defined for the major, minor and revision numbers.

user47559
  • 1,201
  • 8
  • 9
  • I think the submitter doesn't want to (just) enforce a particular value, they want to see what the current value is. – KeyserSoze Oct 13 '09 at 19:00
  • This is the only thing that works for me. I can change the `#if VARIABLE == 123` statement on the fly and the syntax highlighting tells me whether it's the value I think it is or not... – endolith Nov 09 '18 at 21:04
2

Looking at the output of the preprocessor is the closest thing to the answer you ask for.

I know you've excluded that (and other ways), but I'm not sure why. You have a specific enough problem to solve, but you have not explained why any of the "normal" methods don't work well for you.

dwc
  • 24,196
  • 7
  • 44
  • 55
1

BOOST_VERSION is defined in the boost header file version.hpp.

David Harris
  • 2,332
  • 1
  • 13
  • 25
1

Take a look at the Boost documentation as well, regarding how you are using the macro:

In reference to BOOST_VERSION, from http://www.boost.org/doc/libs/1_37_0/libs/config/doc/html/boost_config/boost_macro_reference.html#boost_config.boost_macro_reference.boost_helper_macros:

Describes the boost version number in XXYYZZ format such that: (BOOST_VERSION % 100) is the sub-minor version, ((BOOST_VERSION / 100) % 1000) is the minor version, and (BOOST_VERSION / 100000) is the major version.

bn.
  • 7,739
  • 7
  • 39
  • 54
0

Instead of #error, try redefining the macro, just before it is being used. Compilation will fail and compiler will provide the current value it thinks applies to the macro.

#define BOOST_VERSION blah

tecMav
  • 305
  • 1
  • 2
  • 11