c++c++20c++-conceptsstdformat

C++ concept - allow any std::format() able type?


I have code that allows you to make a std::string format() function on any class and allow that class to be used in calls to std::format(). To do this I say that it requires a method that returns a std::string - can I change this somehow to say format can return "any type that's std::format()able"?

Edit: I've been made aware of std::formattable, but the only examples I can find are how to make sure that the templated type T is itself formattable - I'm still lost on how to specify this about the return type of one of T's functions.

Edit: A suggested use of std::formatter gives:

test.cpp:10:26: error: wrong number of template arguments (1, should be 2)
   10 |   { v.format() } -> std::formattable;
      |                          ^~~~~~~~~~~
In file included from test.cpp:5:
/usr/include/c++/14/format:2547:13: note: provided for ‘template<class _Tp, class _CharT> concept std::formattable’
 2547 |     concept formattable
      |             ^~~~~~~~~~~

Example code:

#include <format>
#include <iostream>

template<typename T>
requires requires (T v) {
  { v.format() } -> std::convertible_to<std::string>;
}
struct std::formatter<T> : formatter<std::string>
{
  auto format(T t, format_context& ctx) const
  {
    return formatter<std::string>::format(t.format(), ctx);
  }
};

struct MyStruct
{
  std::string format() const { return "I'm a struct"; }
};

int main()
{
  MyStruct my_struct;

  std::cout << std::format("outside: {}", my_struct) << std::endl;
}

Solution

  • The syntax of C++20 concepts is designed to test its first argument. In bool conversion contexts like if statements or requires clauses, the full list of arguments is required. However when used as a constraint on a type parameter, or return type of an expression in a requirement expression (the case of OP), or short-hand template arguments the first argument is omitted and replaced with target type. The other arguments cannot be omitted, nor can they have defaults. C++23 concept std::formattable has two arguments; the 2nd one determines the character type of output string/stream:

    template<typename V>
    concept function_formattable =
    requires (V v){
    { v.format() } -> 
        std::formattable<char>;
    };
    

    Generalization for other character types (such as utf8, utf16...) is still incomplete, but the concept is considering future improvements.