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

Rus*_*hPL 4 c++ templates metaprogramming c++11

我想做的是创建:

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.
}
Run Code Online (Sandbox Code Playgroud)

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

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

我越努力实现这一点,我的无知就越多,超过了我的热情.我将不胜感激任何解决部分或全部问题的技巧和/或代码示例.

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

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

注3:通常所有类型安全的printfs都使用不同的方法,我很熟悉这可以通过多次调用轻松完成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"
Run Code Online (Sandbox Code Playgroud)

Moo*_*uck 5

很简单,我们将构建一个编译时字符串,其中" %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];
Run Code Online (Sandbox Code Playgroud)

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

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;};
Run Code Online (Sandbox Code Playgroud)

然后,您需要使用a compile_time_stringbuffer和a类型的函数,并compile_time_stringbuffer使用" %d"或附加的任何内容返回a .由于我们正在处理类型,我甚至不打算定义函数.请注意,我的"结束"专业化会'\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...));
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

它的使用方式如下:

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

以下是执行证明: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);}
};
Run Code Online (Sandbox Code Playgroud)

  • @RushPL我在阅读你的问题之后写了[this](http://coliru.stacked-crooked.com/a/abba15b2ad02130b),认为这可能是有意义的.这是一种完全不同的实现方式,增加了动态. (2认同)