这需要模板。
class Example<class T>
{
...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 的其他 cmets 表明问题仍然存在。让我们把它具体化。
目前我正在使用宏在各种类上定义一堆字段和方法,如下所示:
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"
};