问题
I'm trying to count the number of arguments to a function at compile time (I'm wrapping sprintf up in some templates for compile time checks and type safety). I need to check that the number of arguments matches the number of formatting placeholders at compile time. A first pass at this is pretty simple:
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__));
But this breaks down for certain types of arguments. Namely, when passing a reference.
int x = 0;
int& xRef = x;
String_Format("%", xRef);
The compiler complains about CountArgs(__VA_ARGS__)
because xRef
is not a constant expression. I don't need the value, just the ability to count it. I could wrap it in sizeof
or something similar, but that's tough when all I have is __VA_ARGS__
to work with.
Example: https://godbolt.org/z/Diwffy
回答1:
You can change your macro to something like this
#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));
...
}
回答2:
Maybe using decltype()
and std::integral_constant
?
I mean... you can declare (only declare: no needs of define it) the following function (EDIT: modified, following a Davis Herring's suggestion (thanks!), to accept const references; this permits to works also with non copyable types)
template <typename ... Args>
std::integral_constant<std::size_t, sizeof...(Args)> CArgs (Args const & ...);
and use it, by example
#define bar(num, ...) \
static_assert(num == decltype(CArgs(__VA_ARGS__))::value);
This way you don't use the __VA_ARGS__
values in a static_assert()
but the type returned by a function that accept __VA_ARGS__
.
And the type returned (std::integral_constant<std::size_t, sizeof...(Args)>
) contain the number (accessible through ::value
), as compile-time constant, of the arguments.
The following is a full compiling example
#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);
}
来源:https://stackoverflow.com/questions/58468574/counting-function-arguments-at-compile-time