【问题标题】:How to choose snprinf mask in template function for integral type?如何在模板函数中为整数类型选择 snprinf 掩码?
【发布时间】:2015-02-19 10:14:47
【问题描述】:

我有一个模板函数,它接受一个整数类型的参数并将其复制到堆栈上的字符数组中,std::snprintf

static const size_t size = 256;
char buffer[size];

template <class T, std::enable_if<std::is_integral<T>::value, T>::type>
bool to_array(T integer) {
  auto ret = std::snprint(buffer, size, "%lld", integer);
  return ret > 0;
}

问题在于,如果此函数与 int 类型一起使用,编译器会打印警告,即 "%lld" 掩码需要 long long int 类型。

为了修复它,我使用了boost::fusion::map

using bf = boost::fusion;
using integral_masks = bf::map<
  bf::pair<char, const char*>,
  bf::pair<short, const char*>,
  ....
  bf::pair<unsigned long long, const char*>
>;

integral_masks masks(
  bf::make_pair<char>("%c"),
  bf::make_pair<int>("%d"),
  ....
  bf::make_pair<unsigned long>("%lu")
  bf::make_pair<unsigned long long>("%llu")
);

auto ret = std::snprint(buffer, size, bf::at_key<T>(masks), integer);

这可行,但它看起来有点重,并且boost::fusion 标头显着增加了编译时间。也许有更好更简单的方法来做到这一点?

【问题讨论】:

  • 为什么不直接使用std::to_string
  • @SanderDeDycker 嗯,这是一个简化的例子,我试图避免在这里分配内存。

标签: c++ templates c++11 boost


【解决方案1】:

由于您“试图避免分配”并且无论如何您都在使用 boost:使用 Boost Iostreams 自定义设备

PS为了避免不明显,通过使用流,您可以获得所有好处:

  • 如果您需要 printf 样式或位置参数格式字符串,请与 Boost Format 结合使用
  • 与 Boost Locale 结合用于本地化消息 (gettext) 和格式设置(序数、日期、数字...)

Live On Coliru

#include <array>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>

namespace io = boost::iostreams;

int main()
{
    std::array<char, 128> buf;
    auto b = buf.begin(), e = buf.end();

    io::array_sink as(b, e);
    io::stream<io::array_sink> os(as);

    os << '1' << uint16_t(42) << uint32_t(42) << std::showbase << std::hex << int64_t(-1) << "\n" 
       << std::boolalpha << false << "\n"
       << std::numeric_limits<double>::infinity();

    std::cout << "result '" << std::string(b, os.tellp()) << "'\n";
}

这将在buf 被填充后停止写入输出。

实际上,您可能只需要back_inserter。这样您就可以两全其美:控制分配,同时不受任意限制。

另请参阅std::string::reserve 以了解进一步的优化。您可以根据需要多次重复使用该字符串,而不会产生更多分配。

Live On Coliru

#include <array>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>

namespace io = boost::iostreams;

int main()
{
    std::string buf;
    io::stream<io::back_insert_device<std::string> > os(io::back_inserter(buf));

    os << '1' << uint16_t(42) << uint32_t(42) << std::showbase << std::hex << int64_t(-1) << "\n" 
       << std::boolalpha << false << "\n"
       << std::numeric_limits<double>::infinity();

    os.flush();

    std::cout << "result '" << buf << "'\n";
}

以上两种用法都在仅标头模式下使用 Boost Iostreams(不依赖于 Boost(共享)库的运行时依赖)。

【讨论】:

  • 谢谢,我喜欢第一个代码示例。不幸的是,我不能重用字符串对象。最初,我在代码中使用了static thread_local string,但是当我将其更改为数组时,基准测试显示它的速度提高了 3-6 倍。所以我想我会使用array_sink,它看起来比snprinf 好得多,并且由于我的测试而没有开销。
【解决方案2】:

你可以使用constexpr函数:

constexpr const char* format_of(char) { return "%c"; }
constexpr const char* format_of(int) { return "%d"; }
constexpr const char* format_of(unsigned long) { return "%lu"; }
constexpr const char* format_of(unsigned long long) { return "%llu"; }

Live example

【讨论】:

  • 感谢您的回答!这是一种方法,但对我来说看起来有点奇怪 - 首先创建一个通用函数,然后为每种可能的类型专门化另一个函数以获得掩码:) ...我想也许有一个更“通用”的'怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
相关资源
最近更新 更多