Search code examples
c++templatesc++17variadic-templatesvariadic-macros

Counting function arguments at compile time


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


Solution

  • 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));
        ...
    }