如何在整数类型的模板函数中选择snprinf掩码?

How to choose snprinf mask in template function for integral type?

我有一个模板函数,它接受整数类型的参数并将其复制到堆栈上的字符数组 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" mask reqiures 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 headers 会显着增加编译时间。也许有更好更简单的方法?

您可以使用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

因为你是 "trying to avoid allocation" 并且你仍然在使用 boost:使用 Boost Iostreams 自定义设备

PS Lest it's not obvious, by using streams you get all the goodness:

  • combine with Boost Format if you want printf style or positional argument format strings
  • combine with Boost Locale for localized messages (gettext) and formatting (ordinals, dates, numerics, ...)

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";
}

以上两种用法都在 header-only 模式下使用 Boost Iostream(对 Boost(共享)库没有运行时依赖性)。