C++ 停止预处理器宏扩展
Posted
技术标签:
【中文标题】C++ 停止预处理器宏扩展【英文标题】:C++ Stop Preprocessor Macro Expansion 【发布时间】:2019-05-08 16:57:39 【问题描述】:这是我的示例代码https://godbolt.org/z/VKgKik
#define delete MyCustomDelete(__FILE__, __LINE__), delete
#define CAT(X,Y) CAT2(X,Y)
#define CAT2(X,Y) X##Y
#define CAT_3(X,Y,Z) CAT(X,CAT(Y,Z))
class A
A() = CAT_3(de,le,te);
;
godbolt 示例设置为显示预处理器输出。目标是在预处理程序结束时,我希望输出代码是
class A
A() = delete;
;
目前“ThisShouldNotshowUp”会显示在此处。我认为使用 ## 运算符会阻止预处理器重新扩展,但它没有。
我意识到删除“#define delete”可以解决问题,但我需要这个定义。我创建一个与 delete 同名的宏的原因是因为我希望能够跟踪新闻和删除,如果发生内存泄漏,我可以看到哪一行代码分配了它。因此,这个宏意味着我可以继续在我的代码中使用关键字 delete,并且可以免费填写文件和行号。据我所知,除了定义一个删除宏之外,没有其他方法可以实现此功能。这是问题的症结所在。删除宏为我提供了强大的调试工具,但它删除了一个有用的语言功能供我使用。
【问题讨论】:
1.CAT_2
和 _helper
从未使用过。 2. #define delete
使程序格式错误;不允许将关键字定义为宏名称。
我知道这在技术上是违反标准的,但是我可以做些什么来让它工作吗?让我们假设删除#define delete 是不可能的
在我看来像XY problem。为什么需要这个?
已更新,希望能解决您的问题
@RemyLebeau 我认为原因是一旦进入operator delete
,您将无法再访问delete
表达式所在的源文件和行。
【参考方案1】:
您没有机会通过扩展宏创建预处理标记,该标记是类对象宏的名称。 n3337 的相关部分是[cpp.rescan]
。我在其中引用了第一段的缩短部分。
在替换列表中的所有参数都被替换并且
#
和##
处理已经发生之后[...]。然后重新扫描生成的预处理标记序列 [...] 以替换更多宏名称。
尽管存在问题,delete
在技术上被禁止作为宏名称,但没有办法防止在重新扫描时识别宏名称。
您可能混淆了 ##
运算符确实使用它的 parameters 而不进行扩展的事实,以及 ##
的 result 不进行宏扩展的想法.
【讨论】:
包含自己名称的宏不会自行重新展开。他们在这里这样做是因为他通过嵌套 CAT 中的许多其他宏扩展来传递它。他的宏没有扩展为“ThisShouldNotshowUp”,而是“MyCustomDelete("main.c", 10), delete"。如果宏是“#define delete delete”,它不会以任何方式循环,只会留下“delete”。然而,他当然不能特殊情况下以不同方式扩展令牌宏。如果他使用宏之类的函数,则可能会发生一些事情,但这不会捕获数组删除 @MichaelSpeer 即使这个delete
也不会重新展开;问题不在于任何循环,而在于MyCustomDelete
在语法上无效,其中delete
用于标记已删除的函数。
绝对是。我只是想确保有关宏如何扩展的信息准确无误。【参考方案2】:
您尝试做的事情是不可能的,因为Michael Karcher's answer 指出:#define delete
已经使程序格式错误,并且无法避免扩展类对象宏(在其自身扩展之外)。
但是,对于问题中详述的特定用例,可以使用解决方法。你可以把你的#define delete
放到一个头文件中(我们称之为debug_delete.hxx
),像这样:
#ifdef delete
# undef delete
#endif
#define delete MyCustomDelete(__FILE__, __LINE__), delete
然后,创建另一个头文件(我们称之为normal_delete.hxx
):
#ifdef delete
# undef delete
#endif
请特别注意,这些标头中没有防止多重包含的机制;事实上,我们希望它们可以包含任意次数。
然后,将必须使用= delete;
的代码包装在适当的#include
指令中:
class A
#include "normal_delete.hxx"
A() = delete;
#include "debug_delete.hxx"
~A() delete p;
;
(是的,它很丑陋,但你所做的首先有点丑陋,因此可能需要丑陋的代码才能使其工作)。
【讨论】:
【参考方案3】:大概您想使用宏来打开和关闭删除跟踪。如果您只是在源代码上使用它,而不是尝试将其装配到转换现有 C++ 中,则可以使用类似函数的宏来实现所需的可选跟踪。
#define TRACK_DELETES 0
#if TRACK_DELETES
#define DELETE( a ) \
do MyCustomDelete( __FILE__, __LINE__ ); delete (a); while (0)
#define DELETEALL( a ) \
do MyCustomDelete( __FILE__, __LINE__ ); delete [] (a); while (0)
#else
#define DELETE( a ) do delete (a) ; while(0)
#define DELETEALL( a ) do delete [] (a) ; while(0)
#endif
int main()
DELETE( A );
DELETEALL( B );
return 0;
在gcc -E
下将 TRACK_DELETES 设置为 0 或 1,看看这是否符合您的要求。
您需要单独保留裸露的 delete
关键字,以便正确使用它。
【讨论】:
这个问题是它要求编码者记住使用这个删除宏以上是关于C++ 停止预处理器宏扩展的主要内容,如果未能解决你的问题,请参考以下文章