0

I'm trying to format a boost multiprecision cpp_int using fmt library.

Here's the code I tried

using boost::multiprecision::cpp_int;

int main() {
    cpp_int v("0x8FFFFFFFFFFFFFFFF");
    std::string buffer;   
    fmt::format_to(std::back_inserter(buffer),"{}", v);
    std::cout << buffer;
}

https://godbolt.org/z/M4zKc5hr6 using boost 1.73, fmt 9.1, gcc 7.3

I'm getting compilation error:

error: static assertion failed: Cannot format an argument. To make type T formattable provide a formatter specialization

I stumbled upon this issue https://github.com/fmtlib/fmt/issues/918 that seems to be resolved, so I was expecting fmt to be able to format boost multiprecision numbers. Do I still need to write a formatter specialization?

Vlad Keel
  • 372
  • 2
  • 13

1 Answers1

2

You need to provide a formatter. E.g. you can opt-in to the ostream formatter:

template <> struct fmt::formatter<cpp_int> : fmt::ostream_formatter {};

Live demo: Live On Coliru

#include <boost/multiprecision/cpp_int.hpp>
#include <fmt/format.h>
#include <fmt/ostream.h>
using boost::multiprecision::cpp_int;

template <> struct fmt::formatter<cpp_int> : fmt::ostream_formatter {};

int main() {
    cpp_int v("0x8FFFFFFFFFFFFFFFF");

    std::string buffer;
    fmt::format_to(std::back_inserter(buffer), "Hello world: {}", v);

    fmt::print("Print directly: {} (buffer: {})", v, buffer);
}

Outputs:

Print directly: 166020696663385964543 (buffer: Hello world: 166020696663385964543)

BONUS: Hex, showbase, lower/uppercase

Demonstrating a simple custom formatter that invokes i.str(...) with the required formatting parameters:

Live On Coliru

#include <boost/multiprecision/cpp_int.hpp>
#include <fmt/format.h>
using boost::multiprecision::cpp_int;

template <> struct fmt::formatter<cpp_int> {
    uint8_t showbase_ : 1 = 0, hex_ : 1 = 0, upper_ : 1 = 0;

    auto constexpr parse(auto& ctx) {
        auto e = std::find(ctx.begin(), ctx.end(), '}');
        if (std::string_view f{ctx.begin(), e}; f == "#x")
            showbase_ = hex_ = true;
        else if (f == "#X")
            showbase_ = hex_ = upper_ = true;
        else {
            hex_   = (f == "x") || (f == "X");
            upper_ = (f == "X");
        }
        return e;
    }

    auto format(cpp_int const& i, auto& ctx) {
        auto f = hex_ ? std::ios::hex : std::ios::dec;
        if (showbase_) f |= std::ios::showbase;
        if (upper_)    f |= std::ios::uppercase;
        auto s = i.str(0, f);
        return std::copy(s.begin(), s.end(), ctx.out());
    }
};

int main() {{
    cpp_int v("0x8FFFFFFFFFFFFFFFF");

    fmt::print("{{}}:    {}\n", v);
    fmt::print("{{:}}:   {:}\n", v);
    fmt::print("{{:x}}:  {:x}\n", v);
    fmt::print("{{:#x}}: {:#x}\n", v);
    fmt::print("{{:X}}:  {:X}\n", v);
    fmt::print("{{:#X}}: {:#X}\n", v);
}}

Prints

{}:    166020696663385964543
{:}:   166020696663385964543
{:x}:  8ffffffffffffffff
{:#x}: 0x8ffffffffffffffff
{:X}:  8FFFFFFFFFFFFFFFF
{:#X}: 0X8FFFFFFFFFFFFFFFF
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added a custom formatter that supports hex/dec, showbase and uppercase alpha characters. http://coliru.stacked-crooked.com/a/0e8620c7b1dc2413 – sehe Dec 08 '22 at 22:26
  • Thanks! I extended it a bit to deal with binary representation too. http://coliru.stacked-crooked.com/a/2230baea6e927366 But when I try to incorporate to my own code I get this error: ConversionFunctions.h:23:25: error: specialization of 'template struct fmt::v7::formatter' in different namespace [-fpermissive] fmt/core.h:630:8: error: from definition of 'template struct fmt::v7::formatter' [-fpermissive] I double checked the the specialized formatter is not inside any namespace. – Vlad Keel Dec 09 '22 at 12:25
  • 1
    Erm. The formatter needs to be in `fmt::`. The more portable way to declare that is by opening the namespace like this http://coliru.stacked-crooked.com/a/ee48b914b4cbbc09 (now, libfmt 7.x is a bit oldish, perhaps something changed, but I am assuming that those version namespaces are [`inline using`-declared](https://stackoverflow.com/questions/11016220/what-are-inline-namespaces-for) so it's not relevant?) – sehe Dec 09 '22 at 12:30