更新: 显然,您希望使用模板或基类而不是宏来执行此操作.不幸的是由于各种原因我不能使用模板或基类.
目前我正在使用宏来定义各种类的一堆字段和方法,如下所示:
class Example { // Use FIELDS_AND_METHODS macro to define some methods and fields FIELDS_AND_METHODS(Example) };
FIELDS_AND_METHODS
是一个使用字符串化和令牌粘贴操作符的多行宏.
我想用以下类型的东西替换它
class Example { // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol // defined, to achieve the same result as the macro. #define TYPE_NAME Example #include "FieldsNMethods.h" };
这里我#define类的名称(以前是宏的参数),FieldsNMethods.h
文件包含原始宏的内容.但是,因为我是#including我可以在运行时进入代码,在调试时.
但是我TYPE_NAME
在FieldsNMethods.h
文件中" 预处理"和"标记粘贴" 预处理程序符号时出现问题.
例如,我想定义类的析构函数FieldsNMethods.h
,所以这需要使用如下的值TYPE_NAME
:
~TYPE_NAME() { //... }
但TYPE_NAME
取而代之的是它的价值.
我正在尝试的是什么?我不能直接使用字符串化和令牌粘贴操作符,因为我不在宏定义中.
这迫切需要一个模板.
class Example{ ...class definition... };
直接回答你问题的最后一部分 - "鉴于我不再处于宏定义中,如何将操作符号粘贴和字符串化" - 是"你不能".这些运算符只能在宏中工作,因此您必须编写宏调用才能使它们工作.
补充:
@mackenir说"模板不是一种选择".为什么模板不是一个选项?代码模拟模板是老式的预标准,预模板方式,并且这样做会造成很多痛苦和悲伤.使用模板可以避免这种痛苦 - 尽管有转换操作.
@mackenir问道"有没有办法让宏工作?" 是的,你可以,但你应该使用模板 - 它们更可靠和可维护.要使它适用于宏,那么您必须在包含的标头中的代码中使用函数名称进行宏调用.您需要通过间接级别才能使其正常工作:
#define PASTE_NAME(x, y) PASTE_TOKENS(x, y) #define PASTE_TOKENS(x, y) x ## y #define TYPE_NAME Example int PASTE_NAME(TYPE_NAME, _function_suffix)(void) { ... }
对于标记化和字符串化运算符,这种间接级别通常是必需的习惯用语.
来自@mackenir的其他评论表明仍存在问题.让我们具体化吧.
目前我正在使用宏来定义各种类的一堆字段和方法,如下所示:
class Example { // Use FIELDS_AND_METHODS macro to define some methods and fields FIELDS_AND_METHODS(Example) };
FIELDS_AND_METHODS是一个使用字符串化和令牌粘贴操作符的多行宏.
我想用以下类型的东西替换它
class Example { // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol // defined, to achieve the same result as the macro. #define TYPE_NAME Example #include "FieldsNMethods.h" };
好.为了使这个具体化,我们需要一个FIELDS_AND_METHODS(type)
多行的宏并使用令牌粘贴(我不会处理字符串化 - 但是同样的基本机制将适用).
#define FIELDS_AND_METHODS(type) \ type *next; \ type() : next(0) { } \ type * type ## _next() { return next; }
幸运的是,这声明了"指向参数类型的指针"类型的成员,该类型的构造函数以及返回该指针的方法(在本例中为Example_next).
所以,这可能是宏 - 我们需要替换它,以便'#include'完成相同的工作.
fieldsNmethods.h的内容变为:
#ifndef TYPE_NAME #error TYPE_NAME not defined #endif #define FNM_PASTE_NAME(x, y) FNM_PASTE_TOKENS(x, y) #define FNM_PASTE_TOKENS(x, y) x ## y TYPE_NAME *next; TYPE_NAME() : next(0) { } TYPE_NAME * FNM_PASTE_NAME(TYPE_NAME, _next)() { return next; } #undef FNM_PASTE_NAME #undef FNM_PASTE_TOKENS
请注意,标题不包含多包含保护; 它的存在理由是允许它被多次包括在内.它还取消定义其辅助宏以允许多个包含(好吧,因为重新定义将是相同的,它们是'良性的'并且不会导致错误),并且我将它们FNM_
作为宏的原始命名空间控件作为前缀.这会生成我期望从C预处理器获得的代码.并且G ++不会干扰但会产生一个空的目标文件(因为我的示例代码中没有使用声明的类型).
请注意,除了问题中概述的调用代码之外,这不需要对调用代码进行任何更改.我认为应该使用SPOT"单点真相"原则(或干"不要重复自己")改进这个问题:
#define TYPE_NAME Example class TYPE_NAME { // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol // defined, to achieve the same result as the macro. #include "FieldsNMethods.h" };
您必须添加额外的宏层:
#define STRINGIZE(x) STRINGIZE2(x) #define STRINGIZE2(x) #x #define TOKENPASTE(x, y) TOKENPASTE2(x, y) #define TOKENPASTE2(x, y) x ## y
原因是当你有一个宏时,预处理器通常在执行宏替换之前递归地扩展参数.但是,如果任何参数与字符串化运算符#或令牌粘贴运算符##一起使用,则不会展开它.因此,您需要一个额外的宏层,其中第一层扩展参数,第二层执行字符串化或标记粘贴.
如果参数需要多次扩展(例如#define A B, #define B C, #define C D, STRINGIZE(A)
),那么在应用#或##运算符之前,需要添加更多层.