简体   繁体   中英

How to check a value like “#define VERSION 3.1.4” at compile time?

I am adding compile-time checks to my company's C++ projects to make sure the third-party libraries on all development machines and build servers are up-to-date. Most libraries define something like the following for eg version 3.1.4:

#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_BUILD 4

This is nice and easy to check using static_assert or preprocessor directives.

Now I am looking at a third-party library that defines a single macro instead:

#define VERSION 3.1.4

How can I verify the value of such a macro at compile time?


With C++11, I could use a constexpr string comparison function, and stringify the macro to check it:

constexpr bool static_equal(const char * a, const char * b)
{
    return (*a == *b) && (*a == '\0' || static_equal(a + 1, b + 1));
}

// stringification functions
#define str(x) #x
#define xstr(x) str(x)

static_assert(static_equal(xstr(VERSION), "3.1.4"), "incorrect version of libwhatever");

But we are using Visual Studio 2013 on the Windows machines, so I can only use the subset of C++11 that it supports. Unfortunately constexpr is not supported.

Here is what I am doing now:

#define str(x) #x
#define xstr(x) str(x)

#include xstr(libwhatever.version.is.VERSION.should.be.3.1.4)

Along with this, I add an empty file named libwhatever.version.is.3.1.4.should.be.3.1.4 to the project. So if the version is correct, the preprocessor will successfully include this file. Otherwise, it will fail with "Cannot open 'libwhatever.version.is.2.7.2.should.be.3.1.4', no such file or directory". And failing the build with a somewhat meaningful message is what counts in the end.

Of course this approach is not very flexible; for instance I cannot check for a minimal version, or a range of versions. But for me it is sufficient to be able to check the exact value.

This seems to work with Visual C++ as well as g++. I am not sure whether the behavior is entirely well-defined according to the standard, though.

You can't in the preprocessor, but you can abuse type traits!

VS 2013 seems to support variadic templates. Try using the macro CSTRING at https://stackoverflow.com/a/15912824/2097780 (you should be able to replace constexpr with const and have the code still work) and doing something like:

#define STRT(x) decltype(CSTRING(x))
static_assert(std::is_same<STRT(VERSION), STRT("3.1.4")>::value, "incorrect version of libwhatever");

EDIT: That doesn't work. However, if your compiler compiles this without errors:

extern const char data[] = "abc";
template <char C> struct x {
    static const char c = C;
};
char buf[(int)x<"ABC123"[0]>::c];
int main() { return (int)buf; }

Then you can try this:

#include <type_traits>
#define VERSION 1.2.3
#define STR2(x) #x
#define STR(x) STR2(x)

template <char...> struct ststring;

// https://stackoverflow.com/a/15860416/2097780

#define MACRO_GET_1(str, i) \
    (sizeof(str) > (i) ? str[(i)] : 0)

#define MACRO_GET_4(str, i) \
    MACRO_GET_1(str, i+0),  \
    MACRO_GET_1(str, i+1),  \
    MACRO_GET_1(str, i+2),  \
    MACRO_GET_1(str, i+3)

#define MACRO_GET_16(str, i) \
    MACRO_GET_4(str, i+0),   \
    MACRO_GET_4(str, i+4),   \
    MACRO_GET_4(str, i+8),   \
    MACRO_GET_4(str, i+12)

#define MACRO_GET_64(str, i) \
    MACRO_GET_16(str, i+0),  \
    MACRO_GET_16(str, i+16), \
    MACRO_GET_16(str, i+32), \
    MACRO_GET_16(str, i+48)

#define MACRO_GET_STR(str) MACRO_GET_64(str, 0), 0

static_assert(std::is_same<ststring<MACRO_GET_STR(STR(VERSION))>,
                           ststring<MACRO_GET_STR("1.2.3")>>::value,
              "invalid library version");

If you right click on your project->Properties->Build Events->Pre Build Event You'll see an option that says "Command Line". You can put a call to another program here.

You could write another program in C++ or any language you prefer that checks your file (or any number of files you want) for "#define VERSION 3.1.4". You can abort your build and put any warnings you want in that program.

here is a tutorial: https://dillieodigital.wordpress.com/2012/11/27/quick-tip-aborting-builds-in-visual-studio-based-on-file-contents/

related reading: https://msdn.microsoft.com/en-us/library/e85wte0k.aspx

I tried messing with the preprocessor commands for a long time, and I couldn't find a way to do it using only preprocessor commands.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM