167

我试图弄清楚我的代码认为它正在使用哪个版本的 Boost。我想做这样的事情:

#error BOOST_VERSION

但预处理器不会扩展 BOOST_VERSION。

我知道我可以在程序运行时将其打印出来,并且我知道我可以查看预处理器的输出以找到答案。我觉得在编译期间有一种方法可能会很有用。

4

14 回答 14

180

我知道这是在原始查询之后很长时间,但这可能仍然有用。

这可以在 GCC 中使用 stringify 运算符“#”来完成,但它需要两个阶段。

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

然后可以通过以下方式显示宏的值:

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

请参阅:gcc 在线文档中的 3.4 字符串化。

这个怎么运作:

预处理器理解带引号的字符串并以不同于普通文本的方式处理它们。字符串连接就是这种特殊处理的一个例子。消息杂注需要一个带引号的字符串的参数。当参数有多个组件时,它们必须都是字符串,以便可以应用字符串连接。预处理器永远不能假设一个未引用的字符串应该被视为被引用。如果是这样:

#define ABC 123
int n = ABC;

不会编译。

现在考虑:

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

这相当于

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

这会导致预处理器警告,因为 abc(未加引号)不能与前面的字符串连接。

现在考虑预处理器字符串化(它曾经被称为字符串化,文档中的链接已更改以反映修订后的术语。(顺便说一下,这两个术语同样令人讨厌。正确的术语当然是字符串化。准备好更新您的链接。))运算符。这仅作用于宏的参数,并用双引号括起来的参数替换未扩展的参数。因此:

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

将为 s1 和 s2 分配相同的值。如果你运行 gcc -E 你可以在输出中看到这个。也许 STR 更适合命名为 ENQUOTE。

这解决了在未引用的项目周围加上引号的问题,现在的问题是,如果参数是宏,宏将不会被扩展。这就是需要第二个宏的原因。XSTR 扩展其参数,然后调用 STR 将扩展值放入引号中。

于 2012-05-29T00:41:38.957 回答
127

BOOST_PP_STRINGIZE对于 C++ 来说似乎是一个很好的解决方案,但对于常规 C 来说却不是。

这是我对 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))

以上定义导致:

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"

对于"defined as interger""defined as string""defined but no value"变量,它们工作得很好。仅对于“未定义”变量,它们显示与原始变量名称完全相同。你必须习惯它——或者也许有人可以提供更好的解决方案。

于 2012-04-19T11:29:58.317 回答
61

如果您使用的是 Visual C++,则可以使用#pragma message

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

编辑:感谢LB提供链接

显然,GCC 等效项是(未测试):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)
于 2009-10-13T19:23:01.050 回答
15

据我所知 '#error' 只会打印字符串,实际上你甚至不需要使用引号

您是否尝试过使用“BOOST_VERSION”编写各种故意不正确的代码?也许像“blah[BOOST_VERSION] = foo;” 会告诉您“字符串文字 1.2.1 不能用作数组地址”之类的信息。这不会是一个漂亮的错误消息,但至少它会向您显示相关值。您可以一直玩下去,直到找到确实告诉您值的编译错误。

于 2009-10-13T18:52:50.977 回答
15

没有提升:

  1. 再次定义相同的宏,编译器 HIMSELF 将给出警告。

  2. 从警告中您可以看到先前定义的位置。

  3. vi 先前定义的文件。

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 ()
{

}
于 2018-03-05T11:35:53.227 回答
11

在 Microsoft C/C++ 中,您可以使用内置_CRT_STRINGIZE()来打印常量。我的许多stdafx.h文件都包含以下组合:

#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)) 

并输出如下内容:

_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
于 2018-11-26T21:37:40.557 回答
5
#define a <::BOOST_VERSION>
#include a
MSVC2015:致命错误 C1083:无法打开包含文件:'::106200':没有这样的文件或目录

优点

  • 适用于内置宏
  • 即使preprocess to file启用,即使存在无效令牌也有效:
#define a <::'*/`#>
#include a
MSVC2015:致命错误 C1083:无法打开包含文件:'::'*/`#':没有这样的文件或目录
GCC4.x:警告:缺少终止字符 [-Winvalid-pp-token]
#define a <:: '*/`#>

缺点

  • 有时会因为包含文件路径中的无效字符而失败。可以通过更改前缀来修复(请参阅下面的更新部分)。

更新

对于 GCC 4.7.x 及更低版本,输出会引发错误:

错误:#include 需要“文件名”或 <文件名>

要解决此问题,您可以更改前缀:

#define a <.__cplusplus>
#include a
fatal error: .201103L: No such file or directory
于 2018-01-28T18:09:15.140 回答
3

您还可以预处理源文件并查看预处理器值的计算结果。

于 2009-10-13T18:58:53.973 回答
2

您可以编写一个程序,BOOST_VERSION将其作为构建系统的一部分打印出来并编译和运行。否则,我认为你不走运。

于 2009-10-13T18:30:33.347 回答
2

你在寻找

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

如果 BOOST_VERSION 是一个字符串,就像我假设的那样,那不是很好,但也可能为主要、次要和修订号定义了单独的整数。

于 2009-10-13T18:56:32.913 回答
2

查看预处理器的输出是最接近您要求的答案的事情。

我知道您已经排除了这一点(以及其他方式),但我不确定为什么。您有一个足够具体的问题要解决,但您没有解释为什么任何“正常”方法都不适合您。

于 2009-10-13T19:02:18.913 回答
1

BOOST_VERSION 在 boost 头文件 version.hpp 中定义。

于 2009-10-13T19:04:03.990 回答
1

还请查看 Boost 文档,了解如何使用宏:

参考BOOST_VERSION,来自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

以 XXYYZZ 格式描述 boost 版本号,即: (BOOST_VERSION % 100)是次次要版本,是次要版本, 是主要版本。((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)

于 2009-10-13T19:43:36.130 回答
0

而不是#error,尝试重新定义宏,就在它被使用之前。编译将失败,编译器将提供它认为适用于宏的当前值。

#define BOOST_VERSION 等等

于 2020-07-28T21:22:41.910 回答