如何在编译时检查像“#define VERSION 3.1.4”这样的值?
How to check a value like "#define VERSION 3.1.4" at compile time?
我正在为我公司的 C++ 项目添加编译时检查,以确保所有开发机器和构建服务器上的第三方库都是最新的。大多数图书馆定义如下内容,例如版本 3.1.4:
#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_BUILD 4
使用 static_assert
或预处理器指令这很容易检查。
现在我正在寻找一个定义单个宏的第三方库:
#define VERSION 3.1.4
如何在编译时验证这样一个宏的值?
对于 C++11,我可以使用 constexpr
字符串比较函数,并将宏字符串化以进行检查:
constexpr bool static_equal(const char * a, const char * b)
{
return (*a == *b) && (*a == '[=12=]' || 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");
但是我们在 Windows 机器上使用 Visual Studio 2013,所以我只能使用它支持的 C++11 子集。很遗憾 constexpr
不受支持。
你不能在预处理器中,但你可以滥用类型特征!
VS 2013 似乎支持可变模板。尝试在 处使用宏 CSTRING
(您应该能够将 constexpr
替换为 const
并且代码仍然有效)并执行以下操作:
#define STRT(x) decltype(CSTRING(x))
static_assert(std::is_same<STRT(VERSION), STRT("3.1.4")>::value, "incorrect version of libwhatever");
编辑: 那行不通。但是,如果您的编译器编译没有错误:
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; }
那你可以试试这个:
#include <type_traits>
#define VERSION 1.2.3
#define STR2(x) #x
#define STR(x) STR2(x)
template <char...> struct ststring;
//
#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");
如果您右键单击您的项目->属性->构建事件->预构建事件
您会看到一个显示 "Command Line" 的选项。您可以在这里调用另一个程序。
您可以用 C++ 或任何您喜欢的语言编写另一个程序来检查您的文件(或您想要的任意数量的文件)是否为“#define VERSION 3.1.4”。您可以中止构建并在该程序中放置任何您想要的警告。
相关阅读:https://msdn.microsoft.com/en-us/library/e85wte0k.aspx
我试了很长时间来处理预处理器命令,但我找不到只使用预处理器命令的方法。
这是我现在正在做的事情:
#define str(x) #x
#define xstr(x) str(x)
#include xstr(libwhatever.version.is.VERSION.should.be.3.1.4)
与此同时,我向项目添加了一个名为 libwhatever.version.is.3.1.4.should.be.3.1.4
的空文件。因此,如果版本正确,预处理器将成功包含此文件。否则,它将失败并返回 "Cannot open 'libwhatever.version.is.2.7.2.should.be.3.1.4', no such file or directory"。构建失败并带有某种有意义的消息才是最终的决定因素。
当然这种方式不是很灵活;例如,我无法检查最小版本或一系列版本。但对我来说,能够检查确切的值就足够了。
这似乎适用于 Visual C++ 和 g++。不过,我不确定该行为是否完全符合标准。
我正在为我公司的 C++ 项目添加编译时检查,以确保所有开发机器和构建服务器上的第三方库都是最新的。大多数图书馆定义如下内容,例如版本 3.1.4:
#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_BUILD 4
使用 static_assert
或预处理器指令这很容易检查。
现在我正在寻找一个定义单个宏的第三方库:
#define VERSION 3.1.4
如何在编译时验证这样一个宏的值?
对于 C++11,我可以使用 constexpr
字符串比较函数,并将宏字符串化以进行检查:
constexpr bool static_equal(const char * a, const char * b)
{
return (*a == *b) && (*a == '[=12=]' || 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");
但是我们在 Windows 机器上使用 Visual Studio 2013,所以我只能使用它支持的 C++11 子集。很遗憾 constexpr
不受支持。
你不能在预处理器中,但你可以滥用类型特征!
VS 2013 似乎支持可变模板。尝试在 处使用宏 CSTRING
(您应该能够将 constexpr
替换为 const
并且代码仍然有效)并执行以下操作:
#define STRT(x) decltype(CSTRING(x))
static_assert(std::is_same<STRT(VERSION), STRT("3.1.4")>::value, "incorrect version of libwhatever");
编辑: 那行不通。但是,如果您的编译器编译没有错误:
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; }
那你可以试试这个:
#include <type_traits>
#define VERSION 1.2.3
#define STR2(x) #x
#define STR(x) STR2(x)
template <char...> struct ststring;
//
#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");
如果您右键单击您的项目->属性->构建事件->预构建事件 您会看到一个显示 "Command Line" 的选项。您可以在这里调用另一个程序。
您可以用 C++ 或任何您喜欢的语言编写另一个程序来检查您的文件(或您想要的任意数量的文件)是否为“#define VERSION 3.1.4”。您可以中止构建并在该程序中放置任何您想要的警告。
相关阅读:https://msdn.microsoft.com/en-us/library/e85wte0k.aspx
我试了很长时间来处理预处理器命令,但我找不到只使用预处理器命令的方法。
这是我现在正在做的事情:
#define str(x) #x
#define xstr(x) str(x)
#include xstr(libwhatever.version.is.VERSION.should.be.3.1.4)
与此同时,我向项目添加了一个名为 libwhatever.version.is.3.1.4.should.be.3.1.4
的空文件。因此,如果版本正确,预处理器将成功包含此文件。否则,它将失败并返回 "Cannot open 'libwhatever.version.is.2.7.2.should.be.3.1.4', no such file or directory"。构建失败并带有某种有意义的消息才是最终的决定因素。
当然这种方式不是很灵活;例如,我无法检查最小版本或一系列版本。但对我来说,能够检查确切的值就足够了。
这似乎适用于 Visual C++ 和 g++。不过,我不确定该行为是否完全符合标准。