The library looks cool, but I think that fmtlib is much more flexible in general and it has already supported printing all the possible types. I think that such library will have a way better perspective it would be frontend for fmtlib that would convert pythonic
ptc::print("I am", "very similar to Python", 123, sep = " ", end = "\n" );
to
fmt::print("{} {} {}\n", "I am", "very similar to Python", 123);
It would allow not to repeat the same job for supporting formatting of each std container for PTC authors and more importantly it would allow not to repeat stringification code for each User-Defined Types.
I'm sure users would prefer to write
#include <fmt/format.h>
struct point {
double x, y;
};
template <> struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". The formatter should
// parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'.
// Please also note that this character range may be empty, in case of
// the "{}" format string, so therefore you should check ctx.begin()
// for equality with ctx.end().
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range:
if (it != end && *it != '}') throw format_error("invalid format");
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) const -> decltype(ctx.out()) {
// ctx.out() is an output iterator to write to.
return presentation == 'f'
? fmt::format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y)
: fmt::format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y);
}
};
and then write
fmt::print("point: {}\n", point{0.0, 0.0});
fmt::print("point2: {:e}\n", point{-10.0, 10.0});
ptc::print("point3:", point{6.0, 7.0});
rather than writing formatter overload twice for each library.
Btw, even in this example, I don't see any way to select floating point representation in ptc::print, so it either can't print non-default representation, or values should be somehow wrapped into formatter.