预处理器宏展开到另一个预处理器指令

9

起初我认为我需要这个,但最终我避免使用它。然而,我的好奇心(和对知识的渴望,嗯)让我问:

例如在代码中,是否可以使用预处理器宏来

#include "MyClass.h"

INSTANTIATE_FOO_TEMPLATE_CLASS(MyClass)

扩展到另一个包含文件,比如

#include "MyClass.h"

#include "FooTemplate.h"
template class FooTemplate<MyClass>;

?


3
有一天,您可能想将代码转移到像Linux这样的操作系统中,那里大小写是有区别的(即Foo.h和foo.h是两个不同的文件),那时在Windows上没有被捕获的所有拼写错误都将暴露出来。另外,如果您是一个Linux用户,总有一天会想要采用相反的方式,面临着不同但同样可怕的问题。 - anon
1
我赞同文件名始终使用小写的规则。我之所以这样写是因为我想排除小写转换(类名->文件名)的问题。但这也值得一提,谢谢。+1! - moala
我们的规则是文件名与我们的类型和函数命名方案完全匹配。我们像这里的提问者一样,类型名称FooType将在FooType.h中定义。与任何“风格”指南一样,选择一种风格并坚持使用它。话虽如此,我们在Linux上进行大量开发,因此操作系统会自动强制执行此规则... 嗯嗯嗯... - Richard Corden
好的。那是一个错误。我不喜欢在文件名中使用大写字母,但我喜欢在类型名称中使用大写字母。但为了这个问题,我应该都写成小写。@Richard Corden:让文件系统和编译器强制执行规则可能是暂时的,任何移植到其他操作系统都会完全消除这种安全性。至少,通过强制使用小写文件名规则,可以立即看到风险行为和错误,而不必等到出现任何问题。这是我更喜欢的方式。 - moala
1
@moala。再想一想,无论你做什么(小写或混合大小写匹配内容),都需要由某些外部工具来强制执行。总有可能会有人犯错误,除非你有操作系统支持(就像我的情况一样),否则你必须通过某些脚本/工具来检查这个问题。检查所有小写字母更容易,但是检查名称是否至少与文件中的一个标识符匹配也不是很困难。 <厚颜无耻的插件> 甚至可能有一个静态分析工具可以为您完成这项工作! ;) </ 厚颜无耻的插件>。 - Richard Corden
3个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
14

我相信这是不可能的,因为预处理器是单次遍历的。所以它不能发出其他预处理指令。

具体来说,在C99标准(6.10.3.4第3段)中:

3 结果完全展开的预处理记号序列即使类似于预处理指令也不会被作为预处理指令处理,...

有趣的是,这就是为什么一元运算符_Pragma被添加到c99中的原因。因为宏无法发出#pragma,但_Pragma可以。


当然,它可以发出指令。但它无法在同一预处理器传递中处理它们。 - anon
1
由于##字符在宏中具有特殊含义,我不认为您实际上可以发出指令... - Evan Teran
实际上,GCC预处理器似乎允许“#define X #ifdef X”,其中#ifdef和第二个X之间的空格实际上是一个换行符,并且在运行cpp时实际上会发出#ifdef。由于我刚喝了几杯啤酒,所以建议其他人再检查一下这个问题。 - anon
是的,似乎可以创建一个“类似”指令的东西,但标准明确规定(请参见我的编辑),这样的结构不会被处理为指令。 - Evan Teran
是的,我并不是在建议原帖作者可以做他想要的事情。 - anon

10

C标准对预处理指令有如下规定(C99 - 6.10(2) - 预处理指令):

  

一个预处理指令由一系列预处理标记组成,它以 # 预处理标记开头(在第4个翻译阶段的开始处)...

并且(C99 - 6.10(7)):

  

预处理指令中的预处理标记不受宏展开的影响,除非另有说明。

  

EXAMPLE:

#define EMPTY
EMPTY # include <file.h>

因为第二行的预处理标记序列在翻译阶段4开始时并不是以#开头的预处理指令,尽管在宏EMPTY被替换后它将成为预处理指令,所以不能将宏扩展到#include预处理指令中。由于宏扩展发生在第4阶段,因此宏不能导致某些东西在第4阶段开始时存在。

但我想指出的是,以下内容可以正常工作:

#ifdef WIN32
#define PLATFORM_HEADER "platform/windows/platform.h"
#else
#define PLATFORM_HEADER "platform/linux/platform.h"

#include PLATFORM_HEADER

因为C标准规定了这一点(C99,6.10.2(4) - 源文件包含):

形式为

# include pp-tokens new-line

(that does not match one of the two previous forms)是允许的。 在指令中包含的预处理标记将像普通文本一样进行处理。

(每个当前定义为宏名称的标识符都将被其预处理标记的替换列表替换。)

我查看了标准的那个部分,但我认为那不是最相关的部分。这个例子并不是试图发出指令的例子。相反,它是一个没有 "空格" 前缀的指令示例(即使 EMPTY 宏解析为空格)。 - Evan Teran
1
我同意你从标准中引用的内容更加直接相关,但是当我回答时它并不存在。即使上面的例子与所询问的不同,关于预处理指令需要在第4阶段开始时放置的那一部分也禁止宏展开成有用的预处理指令,即使6.10.3.4(3)更直接地这样说。(换句话说,即使我认为我的答案不是错误的,你的答案显然更好)。 - Michael Burr
抱歉,但我必须选择一个好的答案。另外对你深思熟虑的评论点赞+1,尽可能公平,希望你能理解。 - moala
不需要道歉 - Evan的回答肯定更好。即使不是这种情况,也没有必要道歉。 - Michael Burr

1

所有的预处理指令都会在宏展开之前被解释,因此,不,你不能让一个宏扩展成一个 #include 指令并使其被解释为这样。相反,它将被解释为(错误的)C++代码。


你可能会有这种印象,因为许多指令禁用宏展开其参数,但事实并非如此。宏展开与指令解析同时发生。如果是真的,#if 将无法工作,#undef 也不会产生人们期望的效果。 - zwol

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