Search code examples
c++c++20stdformat

Using std::format with custom types


I'm trying to compile an example from C++20 STL Cookbook. The example is about creating a specialization for std::formatter for a custom type, but I'm getting compilation error. I'm using GCC 13.1 on Windows.

I'm getting the same issue on compiler explorer also (GCC version 13.2) compiler explorer.

#include <format>
#include <string_view>
#include <cstdio>
#include <iostream>

template <typename ... Args>
void print(const std::string_view fmt_str, Args&&... args)
{
    auto fmt_args{ std::make_format_args(args...)};
    std::string outstr{ std::vformat(fmt_str, fmt_args)};
    std::fputs(outstr.c_str(), stdout);
}

struct Fraction
{
    long n;
    long d;
};

template <>
struct std::formatter<Fraction>
{
    template<typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }

    template<typename FormatContext>
    auto format(const Fraction& f, FormatContext& ctx)
    {
        return format_to(ctx.out(), "{0:d}/{1:d}", f.n, f.d);
    }
};

int main()
{
    Fraction f{5, 3};
    print("Frac: {}\n", f);
    // std::cout << std::format("Frac: {}\n", f);
}

Upon compilation I get the following error, I'm unable to understand this compiler error.

C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format: In instantiation of 'static std::__format::_Arg_store<_Context, _Args>::_Element_t std::__format::_Arg_store<_Context, _Args>::_S_make_elt(_Tp&) [with _Tp = Fraction; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>; _Args = {std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle}; _Element_t = std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle>::_Element_t]':
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3252:23:   required from 'std::__format::_Arg_store<_Context, _Args>::_Arg_store(_Tp& ...) [with _Tp = {Fraction}; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>; _Args = {std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle}]'
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3301:14:   required from 'auto std::make_format_args(_Args&& ...) [with _Context = basic_format_context<__format::_Sink_iter<char>, char>; _Args = {Fraction&}]'
.\print.cpp:9:41:   required from 'void print(std::string_view, Args&& ...) [with Args = {Fraction&}; std::string_view = std::basic_string_view<char>]'
.\print.cpp:50:10:   required from here
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3241:38: error: no matching function for call to 'std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::basic_format_arg(Fraction&)'
 3241 |           basic_format_arg<_Context> __arg(__v);
      |                                      ^~~~~
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2996:9: note: candidate: 'template<class _Tp>  requires  __formattable_with<_Tp, _Context, typename _Context::formatter_type<typename std::remove_const<_Tp>::type>, std::basic_format_parse_context<typename _Context::char_type> > std::basic_format_arg<_Context>::basic_format_arg(_Tp&) [with _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>]'
 2996 |         basic_format_arg(_Tp& __v) noexcept
      |         ^~~~~~~~~~~~~~~~
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2996:9: note:   template argument deduction/substitution failed:
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2996:9: note: constraints not satisfied
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format: In substitution of 'template<class _Tp>  requires  __formattable_with<_Tp, _Context, typename _Context::formatter_type<typename std::remove_const<_Tp>::type>, std::basic_format_parse_context<typename _Context::char_type> > std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::basic_format_arg(_Tp&) [with _Tp = std::basic_format_context<std::__format::_Sink_iter<char>, char>]':
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3241:31:   required from 'static std::__format::_Arg_store<_Context, _Args>::_Element_t std::__format::_Arg_store<_Context, _Args>::_S_make_elt(_Tp&) [with _Tp = Fraction; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>; _Args = {std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle}; _Element_t = std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle>::_Element_t]'
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3252:23:   required from 'std::__format::_Arg_store<_Context, _Args>::_Arg_store(_Tp& ...) [with _Tp = {Fraction}; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>; _Args = {std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle}]'
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3301:14:   required from 'auto std::make_format_args(_Args&& ...) [with _Context = basic_format_context<__format::_Sink_iter<char>, char>; _Args = {Fraction&}]'
.\print.cpp:9:41:   required from 'void print(std::string_view, Args&& ...) [with Args = {Fraction&}; std::string_view = std::basic_string_view<char>]'
.\print.cpp:50:10:   required from here
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2197:13:   required for the satisfaction of '__formattable_with<_Tp, _Context, typename _Context::formatter_type<typename std::remove_const<_Tp>::type>, std::basic_format_parse_context<typename _Context::char_type> >' [with _Tp = Fraction; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>]
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2199:7:   in requirements with 'const _Formatter __cf', '_Tp&& __t', '_Context __fc' [with _Formatter = std::formatter<Fraction, char>; _Tp = Fraction; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>]
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2201:20: note: the required expression '__cf.format(__t, __fc)' is invalid
 2201 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |         ~~~~~~~~~~~^~~~~~~~~~~
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format: In instantiation of 'static std::__format::_Arg_store<_Context, _Args>::_Element_t std::__format::_Arg_store<_Context, _Args>::_S_make_elt(_Tp&) [with _Tp = Fraction; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>; _Args = {std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle}; _Element_t = std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle>::_Element_t]':
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3252:23:   required from 'std::__format::_Arg_store<_Context, _Args>::_Arg_store(_Tp& ...) [with _Tp = {Fraction}; _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>; _Args = {std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::handle}]'
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:3301:14:   required from 'auto std::make_format_args(_Args&& ...) [with _Context = basic_format_context<__format::_Sink_iter<char>, char>; _Args = {Fraction&}]'
.\print.cpp:9:41:   required from 'void print(std::string_view, Args&& ...) [with Args = {Fraction&}; std::string_view = std::basic_string_view<char>]'
.\print.cpp:50:10:   required from here
cc1plus.exe: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2808:7: note: candidate: 'std::basic_format_arg<_Context>::basic_format_arg() [with _Context = std::basic_format_context<std::__format::_Sink_iter<char>, char>]'
 2808 |       basic_format_arg() noexcept : _M_type(__format::_Arg_none) { }
      |       ^~~~~~~~~~~~~~~~
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2808:7: note:   candidate expects 0 arguments, 1 provided
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2746:11: note: candidate: 'constexpr std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::basic_format_arg(const std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >&)'
 2746 |     class basic_format_arg
      |           ^~~~~~~~~~~~~~~~
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2746:11: note:   no known conversion for argument 1 from 'Fraction' to 'const std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >&'
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2746:11: note: candidate: 'constexpr std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >::basic_format_arg(std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >&&)'
C:/sw/gcc-13.1.0/mingw64/include/c++/13.1.0/format:2746:11: note:   no known conversion for argument 1 from 'Fraction' to 'std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char> >&&'

Can someone please let me know what is wrong with the above program.


Solution

  • The format member function must accept a const formatter.

    auto format(const Fraction& f, FormatContext& ctx) const
    //                                                 ^^^^^