C++停止预处理器宏扩展

C++ Stop Preprocessor Macro Expansion

本文关键字:扩展 处理器 预处理 C++      更新时间:2023-10-16

这是我的示例代码 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;
};

目前"这不应该出现"显示在那里。我以为使用 ## 运算符会阻止预处理器重新扩展,但事实并非如此。

我意识到删除"#define 删除"可以解决问题,但我需要在那里定义这个。我创建与删除同名的宏的原因是因为我希望能够跟踪新闻和删除,如果发生内存泄漏,我可以看到分配了哪一行代码。因此,此宏意味着我可以继续在我的代码中使用关键字 delete,并且免费填写文件和行号。据我所知,除了定义删除宏之外,没有其他方法可以实现此功能。这是问题的症结所在。删除宏为我提供了一个强大的调试工具,但它删除了一个有用的语言功能供我使用。

您没有机会通过展开宏来创建预处理令牌,该令牌是类似对象的宏的名称。n3337 的相关部分[cpp.rescan]。我引用其中第一段的缩短部分。

替换列表中的所有参数并###处理后 [...]。然后重新扫描生成的预处理令牌序列 [...] 以替换更多宏名称。

尽管存在delete技术上禁止成为宏名称的问题,但无法阻止在重新扫描时识别宏名称。

您可能混淆了##运算符确实使用其参数而不进行扩展的事实,并认为##的结果不会进行宏扩展。

正如Michael Karcher的回答所述,你试图做的事情是不可能的:#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

特别要注意的是,这些标头中没有防止多重包含的机制;事实上,我们希望它们包含任意次数。

然后,将必须在适当的#include指令中使用= delete;的代码包装起来:

class A {
#include "normal_delete.hxx"
A() = delete;
#include "debug_delete.hxx"
~A() { delete p; }
};

(是的,它很丑陋,但你正在做的事情首先有点丑陋,所以可能需要丑陋的代码才能让它工作)。

大概您想使用宏,以便可以打开和关闭删除跟踪。如果您只在源上使用它,而不是尝试安装它来转换现有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关键字,以便可以适当地使用它。