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 e.g. 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.
The LIKE operator is used in a WHERE clause to search for a specified pattern in a column. There are two wildcards often used in conjunction with the LIKE operator: The percent sign (%) represents zero, one, or multiple characters. The underscore sign (_) represents one, single character.
The BETWEEN operator is used to select values within a range.
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 love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With