Search code examples
c++boostfmtboost-multiprecision

format boost multiprecision cpp_int with fmt


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?


Solution

  • 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