重置C/C++预处理器#line指向的物理文件/行号

10
我有一个代码生成器,将使用者编写的代码嵌入到较大的生成文件中。我希望底层编译器在用户代码存在缺陷时提供良好的诊断,但当不应该将生成代码的缺陷归因于源代码时,我也不希望出现这种情况。
我打算在每个用户编写的代码块的开头发出#line lineNum "sourceFile"指令。然而,我找不到任何关于#line指令的文档,提到了一种将__LINE____FILE__“重置”回到生成文件的实际行数的技术,一旦离开了用户提供的代码。理想的解决方案类似于C#预处理器的#line default指令。
我是否只需要跟踪我已经编写了多少行并手动重置它?或者有没有更好的方法,可以传递重置指令或哨兵值给#line,以消除与用户代码的关联?

看起来这可能是以前提出过的, 不过那里没有确定的答案。为了将其与那个区分开来,我还会问一下那里缺乏答案是否随着 C++11 而改变。


你的意图是提供已经预处理过的代码吗? - Mats Petersson
不,它是为了从提供一些RPC接口定义和更高级别的控制流的输入中生成更有用的输出。一个说明性(如果很大)的示例输入是这个。查找用户编写的C++代码中的atomic块,我希望引用它们。实际上,输出会在其他C++源文件中得到#include并且作为这些文件的一部分进行预处理和编译。 - Phil Miller
你能否在文件末尾输出用户代码,这样就不必切换行号/文件名了吗? - Seth Carnegie
在这种情况下,我不相信有解决方案。 - Mats Petersson
@Seth Carnegie:我可以将大部分这段代码放在输出文件的末尾,这肯定会限制出现错误时的影响范围,但并不能完全消除它。感谢您的建议。 - Phil Miller
一些编译器可能有一个扩展可以做你想要的事情,但我不知道有哪些。我还没有仔细阅读2011年的标准,但在C99或C++98中肯定没有这样的功能。跟踪物理行号和“官方”物理文件名是唯一的_可移植_选项。 - zwol
6个回答

8
我之前使用过的技巧是,让我的代码生成器在需要重置行指令时,单独输出一个 #,然后使用一个简单的 awk 脚本来对文件进行后处理并将它们更改为正确的行指令:
#!/bin/awk -f
/^#$/ { printf "#line %d \"%s\"\n", NR+1, FILENAME; next; }
{ print; }

2
是的,你需要记录输出的行数,并知道你要输出到哪个文件中。记住,你指定的行号是下一行的行号。所以如果你已经写了12行,你需要输出#line 14 "filename",因为#line指令将在第13行,所以下一行是14。
在C和C++中,#line预处理器指令没有区别。

1
#line 指令的语法为 #line *digit-sequence* "*s-char-sequence*"(或者只有 #line *digit_sequence*)。 - David Hammen
1
@DavidHammen 糟糕,确实是我把它们的顺序弄错了,已经修复了。我将其更改为“文件名”,因为这样更加直接了当。没有文件名的版本,我认为是毫无意义的,因为提问者想要切换文件名。 - rici

1
假设代码生成器的输入"user.code"包含以下内容:
int foo () {
   return error1 ();
}

int bar () {
   return error2 ();
}

假设您想要增强它,使其基本上看起来像这样:
int foo () {
   return error1 ();
}

int generated_foo () {
   return generated_error1 ();
}

int bar () {
   return error2 ();
}

int generated_bar () {
   return generated_error2 ();
}

除非你不想这样做。你需要在生成的代码中添加#line指令,以便编译器消息指示错误/警告是来自用户代码还是自动生成的代码。#line指令指示下一行代码的源(而不是包含#line指令的行)。

#line 1 "user.code"
int foo () {
   return error1 ();
}

#line 7 "generated_code.cpp"  // NOTE: This is line #6 of generated_code.cpp
int generated_foo () {
   return generated_error1 ();
}

#line 5 "user.code"
int bar () {
   return error2 ();
}

#line 17 "generated_code.cpp" // NOTE: This is line #16 of generated_code.cpp
int generated_bar () {
   return generated_error2 ();
}

1
自从C++20以来,有source_location提供了一个接口,可以获取与__LINE____FILE__相同的信息(还有更多一点)。
它被标记为constexpr,因此应该可以完全用它替代预处理器代码,并在适当时修改信息。

0

@Novelocrat,

我之前在这里提出了这个问题,但没有得到确切的答案。后来我发现,如果在自动生成的代码中插入指向用户代码的行指令,那么这会使自动生成的代码难以重新定位。您必须将自动生成的代码和用户代码放在编译器可以找到它们以报告错误的位置。我认为最好只需在生成的代码中插入用户代码的文件名和行号即可。在良好的文本编辑器中,只需通过将光标放在文件名上按下几个按键即可跳转到文件中的某一行。

例如:在vim中,将光标放在文件名上并按下g-f即可进入该文件,:42则会带您到第42行(假设有错误)。

我在这里发布这个信息,以便其他遇到同样问题的人也可以考虑这种替代方案。


-3

你试过使用 __LINE____FILE__ 吗?我相信它们是从你的 #line 指令中获取的(否则还有什么意义呢?)。

(通过 gcc-4.7.2 和 clang-3.1 进行快速测试可以证实我的猜测)。


1
#line 指令明确记录为重置这些宏。由于预处理器是一种惰性求值器,因此不能将它们保存在宏定义中以便稍后恢复。 - Phil Miller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接