有没有办法在一行上获取__LINE__的值并在其他行上使用该值?

时间:2014-07-20 12:16:21

标签: c-preprocessor

基本上,我想这样做:

#include "foo.h"
#include "bar.h"
static const unsigned line_after_includes = __LINE__;

int main()
{
    foo(line_after_includes);
    bar(line_after_includes);
    return 0;
}

除此之外:

#include "foo.h"
#include "bar.h"
#define LINE_AFTER_INCLUDES __LINE__

int main()
{
    FOO(LINE_AFTER_INCLUDES);
    BAR(LINE_AFTER_INCLUDES);
    return 0;
}

是否可以在LINE_AFTER_INCLUDES定义的行上将__LINE__扩展为LINE_AFTER_INCLUDES的值,以便稍后可以将其与其他宏一起使用?我只是使用第一个代码片段中的变量,但我需要将值作为整数文字,因为我将在switch语句中使用它。另一种方法是

    #define LINE_AFTER_INCLUDES 3 // MAKE SURE THIS IS ON LINE IT'S DEFINED AS!!!

这是丑陋的,难以维护。我会满足于此,但我会做一些比这个例子更复杂的事情......

1 个答案:

答案 0 :(得分:3)

在@ Bathsheba的回答中给出了最好的方法:https://stackoverflow.com/a/24551912/1366431

typedef char LINE_AFTER_INCLUDES[__LINE__];

然后,您可以通过调用__LINE__来访问该声明中sizeof(LINE_AFTER_INCLUDES)的值,因为它的值已被固定为新声明的数组类型的一部分。 sizeof是一个C级编译时常量,适用于switch及相关事物(不需要除法:char的大小保证为1,因为它是单位测量)。唯一的缺点是它是C级而不是预处理器级,因此它不能与#if一起使用或用于标记粘贴。


(原创,我花了很多年的时间打字:)

所以这里的问题是宏定义是懒惰的;即,在实际需要将宏调用插入某种输出之前,不会扩展宏调用。因此,__LINE__的扩张被推迟到为时已晚。

幸运的是,有一种方法可以在预处理器中强制进行评估 - 宏只需要输出某种的输出 - 不一定是程序正文。请记住,宏也可以扩展为形成预处理程序指令的参数 - 然后预处理程序指令 - 由强制扩展控制 - 能够创建进一步的定义。这个方便的观察是Boost的"evaluated slots" functionality的基础,它使用它来提供诸如急切评估和可变预处理器变量槽之类的东西。

不幸的是你不能使用__LINE__的Boost插槽,因为它遇到了同样的问题 - 但我们可以窃取这个想法来产生两个相当不优雅的解决方案。他们每个人都以自己特殊的方式丑陋。

选项1:

使用shell脚本生成大量(几百?)个include个能够使用以下命名方案和内容的文件:

// linedefs/_42_.h
#define LINE_AFTER_INCLUDES 42

...然后使用如下:

#define GEN_LINE_INC(L) _GEN_LINE_S(_GEN_LINE_(L))
#define _GEN_LINE_(L) linedefs/_##L##_.h
#define _GEN_LINE_S(S) _GEN_LINE_S2(S)
#define _GEN_LINE_S2(S) #S

#include "foo.h"
#include "bar.h"

#include GEN_LINE_INC(__LINE__)

int main()
{
    FOO(LINE_AFTER_INCLUDES);
    BAR(LINE_AFTER_INCLUDES);
    return 0;
}

换句话说,使用#include指令强制扩展将__LINE__转换为文件名的宏;包括该文件名为常量生成正确的值。这是不优雅的,因为它需要大量额外的文件和外部工具来生成它们,但它非常简单。

选项2:

#include部分下方的主文件中插入一个又大又丑的块:

#include "foo.h"
#include "bar.h"

// <- This line is the one we generate the number for
#define LINE_BIT_0 0
#define LINE_BIT_1 0
#define LINE_BIT_2 0
#define LINE_BIT_3 0
#define LINE_BIT_4 0
#define LINE_BIT_5 0
#if (__LINE__ - 7) & 1
#  undef LINE_BIT_0
#  define LINE_BIT_0 1
#endif
#if (__LINE__ - 11) >> 1 & 1
#  undef LINE_BIT_1
#  define LINE_BIT_1 (1 << 1)
#endif
#if (__LINE__ - 15) >> 2 & 1
#  undef LINE_BIT_2
#  define LINE_BIT_2 (1 << 2)
#endif
#if (__LINE__ - 19) >> 3 & 1
#  undef LINE_BIT_3
#  define LINE_BIT_3 (1 << 3)
#endif
#if (__LINE__ - 23) >> 4 & 1
#  undef LINE_BIT_4
#  define LINE_BIT_4 (1 << 4)
#endif
#if (__LINE__ - 27) >> 5 & 1
#  undef LINE_BIT_5
#  define LINE_BIT_5 (1 << 5)
#endif
#define LINE_AFTER_INCLUDES (LINE_BIT_0 | LINE_BIT_1 | LINE_BIT_2 | LINE_BIT_3 | LINE_BIT_4 | LINE_BIT_5)


int main() ...

此版本使用#if指令强制扩展__LINE__宏并将其转换为位标志,然后在最后重新组合。这是高度不优雅,因为它依赖于预先计算每个#if和块顶部之间的距离,因为__LINE__在块的过程中评估为不同的值;并且它不能被分解并隐藏在单独的文件中,否则__LINE__将无效。但它仍然有效,并且不需要外部工具。

(如果您有大量#include行,将其扩展到6位以上应该很简单。)

另一方面,这对我来说听起来像是X / Y问题。必须有__LINE__的替代方案才能更好地发挥作用。如果你计算#include d个文件的数量,也许你可以使用类似于每行末尾增加Boost.Counter的行?这样,您也不会受到格式化更改的影响(例如#include部分中的空白行)。

相关问题