在函数参数中捕获 constexpr-ness
Capture constexpr-ness in function arguments
出于各种原因,我正在寻找一种方法来捕获传递给函数的参数的constexpr-ness。解释起来有点棘手,所以我认为代码最能展示我想要实现的目标
#include <vector> // For std::size_t
#include <cstdio>
namespace
{
template<std::size_t N, typename ...TArgs>
constexpr int cstrlen (char const (&s) [N], std::size_t i = 0) noexcept
{
return i < N && s[i] != 0
? 1 + cstrlen (s, i + 1)
: 0
;
}
template<std::size_t N, typename ...TArgs>
inline void silly_printf (char const (&format) [N], TArgs && ...args) noexcept
{
static_assert (cstrlen (format) > 0, "format must not be empty string");
printf (format, std::forward<TArgs> (args)...);
}
}
#define SILLY_PRINTF(format, ...) \
static_assert (cstrlen (format) > 0, "format must not be empty string"); \
printf (format, ##__VA_ARGS__);
int main()
{
// This works but relies on macros
SILLY_PRINTF ("Hello: %d", 1);
// This doesn't work
silly_printf ("Hello: %d", 1);
return 0;
}
我无法让 silly_printf
正常工作。编译器抱怨表达式的计算结果不是常量。我们知道在使用字符串文字调用 silly_print
时它是 constexpr,但是 constexpr-ness 丢失了(顺便说一句,我在这里使用 VS2015)。
我在想也许我可以将 constexpr 添加到参数中(很像 const),但没有成功。
我可以使用宏来解决这个问题(由 SILLY_PRINTF
宏演示),但这感觉很失败。
欢迎提出任何想法。
PS。我真正想要实现的是稍微不那么愚蠢
您不需要使用字符数组引用作为参数。这是我使用的一个,但你需要有 c++14 宽松的 constexpr 规则:
using size_t=decltype(sizeof(int));
namespace util
{
template<typename char_t>
constexpr size_t str_size(const char_t*)noexcept;
}
template
<typename char_t>
constexpr auto
util::
str_size
(const char_t* const a_str)noexcept->size_t
{
const char_t* a_char=a_str;
while(*a_char!=char_t(0))
{
++a_char;
}
return size_t(a_char-a_str);
}
static_assert(util::str_size("hello")==size_t(5),"");
如果您不能使用 c++14,递归版本也可以。您仍然只使用 char 指针作为参数而不是 char 数组引用。
有一个 GNU 扩展(由 g++ 和 clang 支持)允许用户定义以下形式的文字:
template<typename CharT, CharT... Chars>
constexpr void operator"" _something() { }
有了这个,就可以构建一个没有宏的 constexpr-string 类型,可以像这样使用:
constexpr auto str = "testing[=11=]length"_string;
static_assert(str.strlen() == 7, "!");
通过将字符串的所有属性编码到类型中,然后您可以 static_assert 在任何地方使用它,constexpr 与否。
例如,在您的 silly_printf:
template<typename CharT, CharT... Chars, typename... Args>
void silly_printf(const constexpr_string<CharT, Chars...>& format_string, Args&&... args) {
static_assert(format_string.strlen() > 0, "format string must not be empty");
printf(format_string.c_str(), args...);
}
并像这样使用它:
silly_printf("testing %d %s %x embedded[=13=]null"_string, 1, "2", nullptr);
您还可以使用另一个 operator"" _silly_printf()
返回一个函数对象来获得类似 "format string"_silly_printf(args...)
.
的语法
出于各种原因,我正在寻找一种方法来捕获传递给函数的参数的constexpr-ness。解释起来有点棘手,所以我认为代码最能展示我想要实现的目标
#include <vector> // For std::size_t
#include <cstdio>
namespace
{
template<std::size_t N, typename ...TArgs>
constexpr int cstrlen (char const (&s) [N], std::size_t i = 0) noexcept
{
return i < N && s[i] != 0
? 1 + cstrlen (s, i + 1)
: 0
;
}
template<std::size_t N, typename ...TArgs>
inline void silly_printf (char const (&format) [N], TArgs && ...args) noexcept
{
static_assert (cstrlen (format) > 0, "format must not be empty string");
printf (format, std::forward<TArgs> (args)...);
}
}
#define SILLY_PRINTF(format, ...) \
static_assert (cstrlen (format) > 0, "format must not be empty string"); \
printf (format, ##__VA_ARGS__);
int main()
{
// This works but relies on macros
SILLY_PRINTF ("Hello: %d", 1);
// This doesn't work
silly_printf ("Hello: %d", 1);
return 0;
}
我无法让 silly_printf
正常工作。编译器抱怨表达式的计算结果不是常量。我们知道在使用字符串文字调用 silly_print
时它是 constexpr,但是 constexpr-ness 丢失了(顺便说一句,我在这里使用 VS2015)。
我在想也许我可以将 constexpr 添加到参数中(很像 const),但没有成功。
我可以使用宏来解决这个问题(由 SILLY_PRINTF
宏演示),但这感觉很失败。
欢迎提出任何想法。
PS。我真正想要实现的是稍微不那么愚蠢
您不需要使用字符数组引用作为参数。这是我使用的一个,但你需要有 c++14 宽松的 constexpr 规则:
using size_t=decltype(sizeof(int));
namespace util
{
template<typename char_t>
constexpr size_t str_size(const char_t*)noexcept;
}
template
<typename char_t>
constexpr auto
util::
str_size
(const char_t* const a_str)noexcept->size_t
{
const char_t* a_char=a_str;
while(*a_char!=char_t(0))
{
++a_char;
}
return size_t(a_char-a_str);
}
static_assert(util::str_size("hello")==size_t(5),"");
如果您不能使用 c++14,递归版本也可以。您仍然只使用 char 指针作为参数而不是 char 数组引用。
有一个 GNU 扩展(由 g++ 和 clang 支持)允许用户定义以下形式的文字:
template<typename CharT, CharT... Chars>
constexpr void operator"" _something() { }
有了这个,就可以构建一个没有宏的 constexpr-string 类型,可以像这样使用:
constexpr auto str = "testing[=11=]length"_string;
static_assert(str.strlen() == 7, "!");
通过将字符串的所有属性编码到类型中,然后您可以 static_assert 在任何地方使用它,constexpr 与否。 例如,在您的 silly_printf:
template<typename CharT, CharT... Chars, typename... Args>
void silly_printf(const constexpr_string<CharT, Chars...>& format_string, Args&&... args) {
static_assert(format_string.strlen() > 0, "format string must not be empty");
printf(format_string.c_str(), args...);
}
并像这样使用它:
silly_printf("testing %d %s %x embedded[=13=]null"_string, 1, "2", nullptr);
您还可以使用另一个 operator"" _silly_printf()
返回一个函数对象来获得类似 "format string"_silly_printf(args...)
.