Это требует шаблона.
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 указывают на сохраняющиеся проблемы. Давайте сделаем это конкретным.
В настоящее время я использую макрос для определения группы полей и методов в различных классах, например:
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"
};
OK. Чтобы сделать это конкретным, нам нужен макрос 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_
в качестве примитивного элемента управления пространством имен на макросы. Это генерирует код, который я ожидаю от препроцессора Си. и 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"
};