Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using std::format with types that have operator<<

Tags:

c++

c++20

fmt

With the fmt library, you can easily format types that have operator<< defined. As explained here, you extend ostream_formatter with one line of code:

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

Now you can do this:

fmt::format("Foo is {}", Foo());

Is a similar thing possible with std::format()? I have types for which operator<< is already defined, so I'd like to start using them with std::format().

If I try to write my own formatter, I don't see how I can get an ostream from the arguments in the format() member function.

template <typename T>
struct ostream_formatter : std::formatter<T> {
    constexpr auto parse(std::format_parse_context& ctx) {
        return ctx.begin();
    }   
    auto format(const T& obj, auto& ctx) const {
        ostream out = ???;
        out << obj;
        ...
    }
};

Is it better to write the formatters directly and not depend on operator<<?

like image 373
Rob N Avatar asked Apr 09 '26 01:04

Rob N


1 Answers

You can use a string stream to output the value to and then retrieve its content as the result, e.g.

#include <format>
#include <string_view>
#include <sstream>

template <typename Char>
struct basic_ostream_formatter : std::formatter<std::basic_string_view<Char>, Char> {
  template <typename T, typename OutputIt>
  auto format(const T& value, std::basic_format_context<OutputIt, Char>& ctx) const
      -> OutputIt {
    std::basic_stringstream<Char> ss;
    ss << value;
    return std::formatter<std::basic_string_view<Char>, Char>::format(
        ss.view(), ctx);
  }
};

using ostream_formatter = basic_ostream_formatter<char>;

Then

template <> struct std::formatter<ClassWithOstream> : ostream_formatter {};

With a few precautions

  1. This works as-is with MSVC >= 16.11. For MSVC prior to 16.11 that supports std::format, you need to remove the const-qualifier on the format member function.
  2. This works with libc++ (version 15 or later), but you need to replace ss.view() with ss.str().
  3. This may not work with libstdc++ since <format> is not supported (yet).

Some of this is adapted from {fmt} implementation except {fmt} uses its own basic_memory_buffer with a std::basic_ostream instead of std::basic_stringstream.

like image 70
Holt Avatar answered Apr 10 '26 13:04

Holt