Эта тактика подсчета макро аргументов законна?
Я знал о VA_NARGS
макрос, как описано в C Preprocessor, макрос "Перегрузка" некоторое время, но я всегда был отстранен большим количеством шаблонов, которые требуются, чтобы заставить его работать.
Недавно мне понадобилась эта функциональность, и я стиснул зубы и написал весь необходимый макрос-код во всей его "славе".
В моем конкретном случае я могу постоянно полагаться на все аргументы varargs определенного типа. Это привело меня к мысли, что, возможно, есть лучший способ, используя sizeof
и тип массива. Я опробовал это на моей локальной системе, и это, кажется, работает. Тем не менее, я обеспокоен тем, что это решение может быть хрупким (за пределами ограничения типа).
Мои вопросы: действительно ли это безопасное и разумное решение проблемы? Или, может быть: какие проблемы я спрашиваю, если я воспользуюсь этим? И наконец: в той степени, в которой существуют проблемы с попыткой (см. Ниже), существуют ли твики, которые можно применить, чтобы спасти общий подход?
Вот код вместе с демо main()
функция. В этом случае все аргументы varargs должны быть целыми:
#include <stdio.h>
#define ARG_ARRAY(...) ((int[]) { __VA_ARGS__ })
#define ARG_COUNT(...) (sizeof (ARG_ARRAY(__VA_ARGS__)) / sizeof (int))
#define STUFF(label, ...) \
stuff(label, ARG_COUNT(__VA_ARGS__), ARG_ARRAY(__VA_ARGS__))
void stuff(char *label, int count, int *values) {
printf("[%s] count %d", label, count);
for (int i = 0; i < count; i++) {
printf("%s %d", (i == 0) ? ":" : ",", values[i]);
}
printf("\n");
}
int return1(void) {
printf("Called `return1()`.\n");
return 1;
}
int main(int argc, char **argv) {
STUFF("blort");
STUFF("frotz", return1());
STUFF("fizmo", 2 + 3, 6 + 1);
STUFF("glorf", 99, 999, 999);
STUFF("igram", 9, 8, 7, 6, 5, 4, 3, 2, 1);
}
Вот стенограмма:
[blort] count 0
Called `return1()`.
[frotz] count 1: 1
[fizmo] count 2: 5, 7
[glorf] count 3: 99, 999, 999
[igram] count 9: 9, 8, 7, 6, 5, 4, 3, 2, 1
return1()
Распечатка должна проверить, что функция не вызывается дважды.
ОБНОВЛЕНИЕ:
В комментариях было указано, что (int[]) { args }
это C99, но не C++. В моем случае я могу рассчитывать на использование компилятора C для рассматриваемого кода, но все же хорошо знать это конкретное ограничение.
В уже удаленном ответе было указано, что C99 требует, чтобы макро-аргумент varargs был заполнен хотя бы одним фактическим аргументом (хотя я думаю, что спецификация в лучшем случае неоднозначна в этом отношении). Компилятор у меня под рукой (Clang на OS X) принимает код с -Wall
но на самом деле жаловаться -pedantic
, как удачно демонстрирует @2501 в своем комментарии.
-pedantic
также жалуется на массив нулевого размера (расширение без аргументов (int[]) { }
), хотя это можно исправить, добавив фиктивный элемент.
1 ответ
Если все аргументы макроса являются допустимыми и не пустыми выражениями, вы можете попробовать использовать decltype
, вот так:
#include <tuple>
#define NARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
Аргументы макроса не оцениваются. Например:
#include <iostream>
#include <type_traits>
int main(int argc, char *argv[])
{
std::cout << "Have "
<< NARGS("bar", 1, argc, 3, std::declval<int>())
<< " things\n";
}