Эта тактика подсчета макро аргументов законна?

Я знал о 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";
}
Другие вопросы по тегам