Использование макрокоманд или шаблонов для реализации набора функций

У меня есть набор методов, используемых для создания и инициализации набора объектов.
Все они выглядят примерно одинаково, за исключением количества аргументов, передаваемых В этом функция:

ObjectType* CreateObjectType(Arg1 a1, Arg2 arg2, ... ArgN aN)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, a1, a2, ..., aN);
[...]
return object;
}

Обратите внимание, что аргументы не должны использоваться нигде, кроме как для передачи В этом функция.

Я хотел бы найти способ реализовать все это без дублирования кода для каждого типа объекта.


Я попытался использовать переменные макросы, со следующим (недействительным) результатом:

#define CREATE_OBJECT_IMPL(ObjectType, ...)    \
ObjectType* Create##ObjectType##(__VA_ARGS__)  \
{                                              \
ObjectType* object = new ObjectType();     \
[...]
object->Init(this, ##__VA_ARGS__);         \
[...]
return object;                             \
}

// This is the result I am trying to achieve :
CREATE_OBJECT_IMPL(MyFirstObject, bool, float)
CREATE_OBJECT_IMPL(MySecondObject, int)
CREATE_OBJECT_IMPL(MyThirdObject)

Теперь в этой реализации я дважды использовал VA_ARGS, оба раза неправильно:

  • В первом случае я хочу получить список аргументов с указанными мною типами (Arg1 a1, Arg2 a2 …)

  • Во втором случае я хочу назвать эти аргументы их именами (Init (a1, a2 …)).


Я пытался использовать шаблоны с переменным числом аргументов:

template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args);
[...]
return object;
}

#define CREATE_OBJECT_IMPL(ObjectType, ...)                     \
ObjectType* Create##ObjectType##(__VA_ARGS__)                   \
{                                                               \
return CreateObject<ObjectType, __VA_ARGS__>(__VA_ARGS__);  \
}

…но это, кажется, не работает, я получаю следующую ошибку в строке определения шаблона:

ошибка C2143: синтаксическая ошибка: отсутствует ‘,’ перед ‘…’

ошибка C2065: «Аргументы»: необъявленный идентификатор

Я использую VS2012.

Я все еще мог написать N одинаковых макросов для каждого количества аргументов, однако мне было интересно, есть ли способ получить тот же результат без дублирования кода?

5

Решение

Есть несколько способов решить проблему. Во-первых, вы можете использовать типизированное выражение в макросе, чтобы вы могли проанализировать тип. Итак CREATE_OBJECT_IMPL будет называться так:

CREATE_OBJECT_IMPL(Object, (Arg1) arg1, (Arg2) arg2)

Вот несколько макросов, которые извлекают тип и удаляют его:

#define EAT(x)
#define REM(x) x
#define STRIP(x) EAT x
#define PAIR(x) REM x

Эти макросы работают следующим образом. Когда ты пишешь STRIP((Arg1) arg1) это расширится до arg1, А когда пишешь PAIR((Arg1) arg1) это расширится до Arg1 arg1, Теперь, вам нужно будет применить эти макросы к каждому передаваемому аргументу, поэтому вот простой APPLY макрос, который позволит вам сделать до 8 аргументов:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) CAT(APPLY_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define APPLY_1(m, x1) m(x1)
#define APPLY_2(m, x1, x2) m(x1), m(x2)
#define APPLY_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define APPLY_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define APPLY_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define APPLY_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define APPLY_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define APPLY_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)

Затем вы можете определить CREATE_OBJECT_IMPL как это:

#define CREATE_OBJECT_IMPL(ObjectType, ...) \
ObjectType* Create##ObjectType(APPLY(PAIR, __VA_ARGS__))  \
{ \
ObjectType* object = new ObjectType(); \
[...] \
object->Init(this, APPLY(STRIP, __VA_ARGS__)); \
[...] \
return object; \
}

Конечно, вам могут понадобиться некоторые обходные пути для этих макросов, если вы используете их в Visual Studio. Конечно, лучшее решение — написать шаблонную функцию. Так что вы бы назвали свой CreateObject как это:

ObjectType* obj = CreateObject<ObjectType>(arg1, arg2, arg3);

В C ++ 11 вы можете использовать шаблоны varidiac, например:

template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args...);
[...]
return object;
}

Но если ваш компилятор не поддерживает шаблоны Vidiac, вы можете использовать Boost.PP чтобы генерировать перегрузки для 10 аргументов (или больше, если вам нужно):

#define GENERATE_OBJS_EACH(z, n, data) \
template<class ObjectType, BOOST_PP_ENUM_PARAMS_Z(z, n, class Arg)> \
ObjectType* CreateObject(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, Arg, arg))  \
{ \
ObjectType* object = new ObjectType(); \
[...] \
object->Init(this, BOOST_PP_ENUM_PARAMS_Z(z, n, arg)); \
[...] \
return object; \
}
/* Generate CreateObject template for up to 10 arguments */
BOOST_PP_REPEAT_FROM_TO_1(1, 10, GENERATE_OBJS_EACH, ~)

Редактировать: Вот обходные пути, которые вам понадобятся, чтобы заставить вышеуказанные макросы работать в msvc:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS_MSVC_WORKAROUND(x) NARGS_SEQ x
#define NARGS(...) NARGS_MSVC_WORKAROUND((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1))

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT_MSVC_WORKAROUND(x) PRIMITIVE_CAT x
#define CAT(x, y) CAT_MSVC_WORKAROUND((x, y))

/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) APPLY_MSVC_WORKAROUND(CAT(APPLY_, NARGS(__VA_ARGS__)), (macro, __VA_ARGS__))
#define APPLY_MSVC_WORKAROUND(m, x) m x
...
6

Другие решения

Вы должны положить … после обоих Args а также args
Вот:

ObjectType* CreateObject(Args args)

и здесь:

object->Init(this, args);

тогда код должен быть:

template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args...);
[...]
return object;
}

Другая проблема заключается в том, что Visual Studio 2012 не поддерживает шаблоны с переменным числом аргументов, но в выпуске от ноября 12 года проверьте, есть ли у вас самая последняя версия компилятора.

Также вам не нужны переменные макросы для воссоздания новых функций, вы можете указать ObjectType следующим образом:

ObjectType* obj = CreateObject<ObjectType>(foo, 1, "hi");
0

По вопросам рекламы [email protected]