在编译时计算函数参数

时间:2019-10-19 22:35:28

标签: c++ templates c++17 variadic-templates variadic-macros

我试图在编译时计算函数的参数数量(我将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 个答案:

答案 0 :(得分:2)

您可以将宏更改为类似的内容

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

答案 1 :(得分:2)

也许使用decltype()std::integral_constant吗?

我的意思是...您可以声明(仅声明:无需对其进行定义)以下函数(编辑:根据Davis Herring的建议(谢谢!)进行了修改,可以接受const引用;这允许与const一起使用不可复制类型)

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);
}
相关问题