std :: формат пользовательских типов? - PullRequest
6 голосов
/ 25 января 2020

В C ++ 20 - как сделать пользовательский тип совместимым с std::format?

Например, допустим, у меня есть тип с именем Point:

struct Point {
    int x;
    int y;
};

с определением operator<<:

inline std::ostream&
operator<<(std::ostream& o, Point pt)
{ return o << "[" << pt.x << << ", " << pt.y << "]"; }

, тогда следующая программа выдаст Hello [3, 4]!?

int main() {
   Point pt{3,4};
   std::cout << std::format("Hello {}!\n", pt);
}

Если да - почему и как?

Если нет - что мне нужно добавить к определению Point, чтобы оно заработало?

Ответы [ 2 ]

6 голосов
/ 26 января 2020

std::format не поддерживает operator<<, вам нужно указать специализацию formatter для вашего типа (Point). Самый простой способ сделать это - повторно использовать один из существующих форматеров, например, std::formatter<std::string>:

template <>
struct std::formatter<Point> : std::formatter<std::string> {
  auto format(Point p, format_context& ctx) {
    return formatter<string>::format(
      std::format("[{}, {}]", p.x, p.y), ctx);
  }
};

. Это даст вам все спецификации формата, поддерживаемые std::string из коробки. Вот пример форматирования Point с выравниванием по центру, дополненным '~' до 10 символов:

auto s = std::format("{:~^10}", Point{1, 2});
// s == "~~[1, 2]~~"

, чего нетривиально достичь с помощью iostreams.

5 голосов
/ 25 января 2020

Вы должны специализироваться std::formatter для вашего типа.

namespace std
{
    template<class CharT>
    struct formatter<Point, CharT>
    {  
        template <typename FormatParseContext>
        auto parse(FormatParseContext& pc)
        {
            // parse formatter args like padding, precision if you support it
            return pc.end(); // returns the iterator to the last parsed character in the format string, in this case we just swallow everything
        }

        template<typename FormatContext>
        auto format(Point p, FormatContext& fc) 
        {
            return std::format_to(fc.out(), "[{}, {}]", p.x, p.y);
        }
    };
}

Я не думаю, что оператор ostream будет работать, но у меня нет источников для поддержки этого утверждения.

...