用于调用 printf 的 C++11 编译时格式字符串文字构造

2023-11-23

我想做的是创建:

template<Args... args)>
int println(Args...) {
  // implementation which calls:
  // printf("<string literal format string at compile time>", args...);
  // additional perk would be compile time type checking
  // I expect to provide a format string for each type by some template
  // specialization.
}

我一直在分析编译时字符串文字的两个有趣的工作:

  • 编译时内存对齐的字符串文字

    https://stackoverflow.com/a/22067775/403571

  • 100% constexpr 字符串实现

    http://sourceforge.net/p/constexprstr/code/HEAD/tree/no-pp-constexpr_string.cpp

基本上,我或多或少能够静态推断格式所需字符串文字的长度,但仅此而已,因为编译器拒绝将我的工作视为 constexpr。另一个大问题是,当使用上面链接中的 constexpr 字符串时,编译器永远无法推断结果字符串的大小。

我越是努力实现这一目标,我的无知就越超过我的热情。如果有任何提示和/或代码示例能够解决此类实现的部分或全部问题,我将不胜感激。

注意:我并不是在寻找有关使用不同形式的日志记录(例如通过 cout)的建议。

注意2:不应该使用任何std::string,因为它们是运行时的

注意3:通常所有类型安全的 printf 都使用不同的方法,并且我熟悉这可以通过多次调用轻松完成printf。我想在一次通话中完成此操作。我也知道缓冲区可以增量构建,但这个问题是关于构造格式字符串的。 :)

更新:基本上代码需要实现的部分是

constexpr const char* formatString = build_compile_time_format_string(args...);
// build_compile_time_format_string(3, "hi", -3.4)
//     should evaluate to "%d %s %f"
//          or to "%d hi %f"

很简单,我们将使用以下内容构建一个编译时字符串" %d"或者对于每种类型,连接一个'\n',并去掉前导空格。

首先,我们需要一个类型来用作编译时字符串:

template<char...cs> struct compile_time_string 
{static constexpr char str[sizeof...(cs)+1] = {cs...,'\0'};};
template<char...cs>
const char compile_time_string<cs...>::str[sizeof...(cs)+1];

为了防止中间步骤生成无意义的缓冲区,字符串生成器:

template<char...cs> struct compile_time_stringbuilder 
{typedef compile_time_string<cs...> string;};

//remove leading spaces from stringbuilder
template<char...cs> struct compile_time_stringbuilder<' ', cs...>
{typedef typename compile_time_stringbuilder<cs...>::string string;};

然后,您需要需要一个函数compile_time_stringbuffer和一个类型,并返回一个compile_time_stringbuffer" %d"或附加的任何内容。由于我们正在处理类型,所以我什至不需要定义函数。请注意,我的“结束”专业化连接了'\n'适合你的性格

template<char...cs, class...Ts> 
compile_time_stringbuilder<cs...,'\n'> concatenate_compile_time_format_string(compile_time_stringbuilder<cs...>);

template<char...cs, class...Ts> 
auto concatenate_compile_time_format_string(compile_time_stringbuilder<cs...>,int,Ts...args)
-> decltype(concatenate_compile_time_format_string(compile_time_stringbuilder<cs...,' ','%','d'>(),args...));

template<char...cs, class...Ts> 
auto concatenate_compile_time_format_string(compile_time_stringbuilder<cs...>,const char*,Ts...args)
-> decltype(concatenate_compile_time_format_string(compile_time_stringbuilder<cs...,' ','%','s'>(),args...));

template<char...cs, class...Ts> 
auto concatenate_compile_time_format_string(compile_time_stringbuilder<cs...>,double,Ts...args)
-> decltype(concatenate_compile_time_format_string(compile_time_stringbuilder<cs...,' ','%','f'>(),args...));

最后,一个有用且易于使用的界面。

template<class...Ts>
constexpr const char* build_compile_time_format_string()
{
    using compile_time_stringbuilder = decltype(concatenate_compile_time_format_string(compile_time_stringbuilder<>(),std::declval<Ts>()...));
    using compile_time_string = typename compile_time_stringbuilder::string;
    return compile_time_string::str;
}

它的用法如下:

template<class...Args>
void println(Args...args) {
    constexpr const char* formatString = build_compile_time_format_string<Args...>();
    std::cout << formatString;
}

这是执行证明:http://coliru.stacked-crooked.com/a/16dc0becd3391aaa


完全没有必要,充实起来可能会很有趣compile_time_string大致匹配的接口const std::string,沿着这些思路:

template<char...cs> struct compile_time_string 
{
    static constexpr char str[sizeof...(cs)+1] = {cs...,'\0'};
    constexpr size_t size() {return sizeof...(cs);}
    constexpr char* begin() {return str;}
    constexpr char* end() {return str+sizeof...(cs);}
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用于调用 printf 的 C++11 编译时格式字符串文字构造 的相关文章

随机推荐