用c++可变模板包装cstdio打印函数



我正在为嵌入式系统编写一个轻量级解析库,并试图避免使用iostream。我想做的是将变量写入像vsnprintf()这样的缓冲区,但我不想指定格式字符串,而是应该从传递给可变模板包装器的参数中推断出格式字符串。下面是一个例子:

#include <cstddef>
#include <string>
#include <cstdio>

template <typename... Ts>
void cpp_vsnprintf(char* s, size_t n, Ts&&... arg)
{
std::vsnprintf(s, n, /*how to deduce format string?*/, arg...);
}
int main()
{
char buf[100];
cpp_vsnprintf(buf, 100, "hello", 3, '2', 2.4);
printf(buf);
}

我正在寻找一个高性能的解决方案,也许格式字符串可以组成在编译时间? 或者是否有一个stl函数可以完全满足我的要求?

可以这样写:

void AddFormatSpec(int, std::string& format) {
format += "%d";
}
void AddFormatSpec(double, std::string& format) {
format += "%g";
}
// Add overloads to taste.
template <typename... Ts>
void cpp_vsnprintf(char* s, size_t n, Ts&&... arg)
{
std::string format;
(AddFormatSpec(arg, format), ...);
std::snprintf(s, n, format.c_str(), arg...);
}

演示

我从这个帖子中得到了一些灵感

#include <cstdio>
template<class T> struct format;
template<class T> struct format<T*>       { static constexpr char const * spec = "%p";  };
template<> struct format<int>             { static constexpr char const * spec = "%d";  };
template<> struct format<double>          { static constexpr char const * spec = "%.2f";};
template<> struct format<const char*>     { static constexpr char const * spec = "%s";  };
template<> struct format<char>            { static constexpr char const * spec = "%c";  };
template<> struct format<unsigned long>   { static constexpr char const * spec = "%lu"; };

template <typename... Ts>
class cxpr_string
{
public:
constexpr cxpr_string() : buf_{}, size_{0}  {
size_t i=0;
( [&]() {
const size_t max = size(format<Ts>::spec);
for (int i=0; i < max; ++i) {
buf_[size_++] = format<Ts>::spec[i];
}
}(), ...);
buf_[size_++] = 0;
}
static constexpr size_t size(const char* s)
{
size_t i=0;
for (; *s != 0; ++s) ++i;
return i;
}
template <typename... Is>
static constexpr size_t calc_size() {
return (0 + ... + size(format<Is>::spec));
}
constexpr const char* get() const {
return buf_;
}
static constexpr cxpr_string<Ts...> ref{};
static constexpr const char* value = ref.get();
private:
char buf_[calc_size<Ts...>()+1] = { 0 };
size_t size_;
};
template <typename... Ts>
auto cpp_vsnprintf(char* s, size_t n, Ts... arg)
{
return snprintf(s, n, cxpr_string<Ts...>::value, arg...);
}

int main()
{
char buf[100];
cpp_vsnprintf(buf, 100, "my R", 2, 'D', 2, '=', 3.5);
printf(buf);
}

演示输出:

my R2D2=3.50

您可以看到格式字符串被整齐地打包到二进制文件中:

.string "%s%d%c%d%c%.2f"
.zero   1
.quad   15

最新更新