Подсчет аргументов функции во время компиляции - PullRequest
4 голосов
/ 20 октября 2019

Я пытаюсь подсчитать количество аргументов функции во время компиляции (я обертываю sprintf в некоторых шаблонах для проверки времени компиляции и безопасности типов). Мне нужно проверить, что количество аргументов совпадает с количеством заполнителей форматирования во время компиляции. Первый проход в этом довольно прост:

template <typename... Args>
constexpr u32
CountArgs(Args&&... args)
{
    return sizeof...(args);
}

constexpr u32
CountFormatSpecifiers(c8* format);

template <typename... Args>
c8*
String_FormatImpl(c8* format, Args&&... args);

#define String_Format(format, ...) \
    String_FormatImpl(format, __VA_ARGS__); \
    static_assert(CountFormatSpecifiers(format) == CountArgs(__VA_ARGS__));

Но это не работает для определенных типов аргументов. А именно, при передаче ссылки.

int x = 0;
int& xRef = x;
String_Format("%", xRef);

Компилятор жалуется на CountArgs(__VA_ARGS__), поскольку xRef не является константным выражением. Мне не нужно значение, просто умение считать его. Я мог бы обернуть это в sizeof или что-то подобное, но это сложно, когда все, что у меня есть, это __VA_ARGS__ для работы.

Пример: https://godbolt.org/z/Diwffy

Ответы [ 2 ]

2 голосов
/ 20 октября 2019

Может быть, с использованием decltype() и std::integral_constant?

Я имею в виду ... вы можете объявить (только объявить: не нужно его определять) следующую функцию (РЕДАКТИРОВАТЬ: модифицировано, следуя предложению Дэвиса Херринга)(спасибо!), чтобы принять константные ссылки; это позволяет работать также с не копируемыми типами)

template <typename ... Args>
std::integral_constant<std::size_t, sizeof...(Args)> CArgs (Args const & ...);

и использовать его, например

#define bar(num, ...) \
    static_assert(num == decltype(CArgs(__VA_ARGS__))::value);

Таким образом, вы не можетеиспользуйте значения __VA_ARGS__ в static_assert(), но тип, возвращаемый функцией, которая принимает __VA_ARGS__.

И возвращаемый тип (std::integral_constant<std::size_t, sizeof...(Args)>) содержит число (доступно через ::value),в качестве константы времени компиляции аргументов.

Ниже приведен полный пример компиляции

#include <type_traits>

template <typename ... Args>
std::integral_constant<std::size_t, sizeof...(Args)> CArgs (Args const & ...);

#define bar(num, ...) \
    static_assert(num == decltype(CArgs(__VA_ARGS__))::value);

int main()
{
   int x = 0;
   int& xRef = x;

   //..VV  number of the following arguments    
   bar(3u, x, xRef, 42);
}
2 голосов
/ 20 октября 2019

Вы можете изменить свой макрос на что-то вроде этого

#define String_Format(format, ...) \
    String_FormatImpl<CountFormatSpecifiers(format)>(format, __VA_ARGS__);

template <std::size_t I, typename... Args>
void String_FormatImpl(const char* format, Args&&...) {
    static_assert(I == sizeof...(Args));
    ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...