Заявление об ограничении ответственности 1:
Честно говоря, я не советую вам делать такой механизм за макросами. Они станут тяжелым бременем для обслуживания, а отдача будет минимальной. Если вы можете сделать это с другими, более поддерживаемыми абстракциями, это будет лучше для вас в долгосрочной перспективе.
Заявление об ограничении ответственности 2:
Я пишу это без компилятора в данный момент. У меня может быть синтаксическая ошибка, но это общие c строительные блоки для этого решения.
В этом заявлении об отказе от ответственности сказано, что это можно сделать, но не очень хорошо.
У вас есть несколько проблем, которые необходимо решить с помощью множества уловок с макросами:
- Вы хотите, чтобы расширение содержало размер
__VA_ARGS__ / 2
(во время раскрытия) - Вы хотите, чтобы расширение
__VA_ARGS__
производило Variant(<arg 1>), Variant(<arg 3>), Variant(<arg 5>), Variant()
- Вы хотите, чтобы расширение
__VA_ARGS__
до произвести Variant[size]{args[0], args[1], args[2], ...}
Для начала я собираюсь создать помощник с именем JOIN
:
#define JOIN(a, b) JOIN_H(a, b)
#define JOIN_H(a, b) a ## b
Это может показаться глупым, но на самом деле он делает гарантирует, что объединяемые макросы будут оценены перед тем, как они будут объединены - так что вызываемые макрофункции будут правильно создавать экземпляр объединения с их результатом , а не полным именем.
Получение размера __VA_ARGS__ / 2
Для получения размера __VA_ARGS__
обычно требуются два макроса:
- Один, который передает
__VA_ARGS__, N, N-1, N-2, ...
во вспомогательный макрос, и - Другой, который извлекает
N
в конце.
* 105 3 * Что-то вроде:
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_VA_ARGS_H(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N
Это работает, потому что первый передает все аргументы __VA_ARGS__
и считает в обратном порядке от N-го числа, а затем мы извлекаем N
.
В вашем случае вам нужен __VA_ARGS__ / 2
, поэтому вам нужно будет удвоить эти аргументы
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0)
#define COUNT_VA_ARGS_H(_1, _1, _2, _2, _3, _3, _4, _4, _5, _5, _6, _6, _7, _7, _8, _8, _9, _9, _10, _10, N,...) N
Заставить __VA_ARGS__
создать Wrap(<arg 1>), Wrap(<arg 3>), ...
В отличие от шаблонов Variadi c C ++, макросы не могут иметь выражения раскрытия, в которые можно заключить каждый аргумент. Чтобы смоделировать это в макросах, вам в значительной степени нужно явно указать N расширений, а затем, чтобы вызвать это, вам нужно будет объединить результат одного макроса для его вызова.
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0) wrap(x0)
...
#define WRAP_VA_ARGS_10(wrap,x0,x1, ..., x10) wrap(x0), wrap(x1), ..., wrap(x10)
// Call into one of the concrete ones above
#define WRAP_VA_ARGS(wrap, __VA_ARGS__) JOIN(WRAP_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS__))(__VA_ARGS__)
Поскольку выражение на самом деле хочет каждый другой аргумент в нем, вам снова нужно будет удвоить аргументы:
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0type,x0) wrap(x0)
#define WRAP_VA_ARGS_2(wrap,x0type,x0,x1type,x1) wrap(x0), wrap(x1)
...
Вызов WRAP_VA_ARGS(Variant, int, A, float, B)
теперь создаст Variant(A), Variant(B)
Создание списка значений индекса
Подобно описанному выше обертыванию, вам нужно будет найти способ сгенерировать список чисел и обернуть его. Опять же, это должно быть делегировано счетной оболочке
#define WRAP_COUNT_VA_ARGS_0(wrap)
#define WRAP_COUNT_VA_ARGS_1(wrap) wrap[0]
#define WRAP_COUNT_VA_ARGS_2(wrap) wrap[0], wrap[1]
...
#define WRAP_COUNT_VA_COUNT_ARGS(wrap, ...) JOIN(WRAP_COUNT_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS))(wrap)
Вызов WRAP_COUNT_VA_COUNT_ARGS(args, int, A, float, B)
должен генерировать args[0], args[1]
Собирая все вместе
Предупреждение о триггере: это будет некрасиво
#define DECLARE_FUNCTION(name, ...) \
void name(__VA_ARGS__) { \
JOIN(name, _PROXY)((Variant[COUNT_VA_ARGS(__VA_ARGS__)+1]) {WRAP_VA_ARGS(Variant,__VA_ARGS__), Variant()}); \
} \
void JOIN(name, _PROXY)(const Variant (&args)[COUNT_VA_ARGS(__VA_ARGS__) + 1]) { \
JOIN(name, _HANDLER)(WRAP_COUNT_VA_COUNT_ARGS(args, __VA_ARGS__)); \
} \
void JOIN(name, _HANDLER)(__VA_ARGS__) { \
\
}
Если повезет, пример DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
должен дать:
void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[3+1]{Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[3+1]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {
}
Примечание: массив создан постоянным выражением 3 + 1
, поскольку нам нужно выполнить эту арифметику c, чтобы учесть Variant()
в конце вызова myFunction_PROXY
Не делать макросов. Макросы плохие, ммм?