简体   繁体   English

如何在编译时检查像“#define VERSION 3.1.4”这样的值?

[英]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. 我正在为我公司的C ++项目添加编译时检查,以确保所有开发机器和构建服务器上的第三方库都是最新的。 Most libraries define something like the following for eg version 3.1.4: 大多数库定义类似于以下内容的版本,例如版本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. 使用static_assert或预处理程序指令很容易检查。

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: 使用C ++ 11,我可以使用constexpr字符串比较函数,并将宏字符串化以检查它:

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. 但我们在Windows机器上使用Visual Studio 2013,因此我只能使用它支持的C ++ 11子集。 Unfortunately constexpr is not supported. 不幸的是, constexpr不受支持。

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. 与此同时,我将一个名为libwhatever.version.is.3.1.4.should.be.3.1.4的空文件添加到项目中。 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". 否则,它将失败,“无法打开'libwhatever.version.is.2.7.2.should.be.3.1.4',没有这样的文件或目录”。 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++. 这似乎适用于Visual C ++以及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. VS 2013似乎支持可变参数模板。 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: 尝试在https://stackoverflow.com/a/15912824/2097780上使用宏CSTRING (您应该能够用const替换constexpr并使代码仍然有效)并执行以下操作:

#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". 您可以用C ++或您喜欢的任何语言编写另一个程序来检查您的文件(或任何数量的文件)以查找“#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/ 这是一个教程: 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 相关阅读: 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. 我试过很长时间搞乱预处理程序命令,我找不到使用预处理程序命令的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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