Pprint: Pretty Printer for Modern C++
github.com
github.com
One thing that drives me crazy about C++ is that if you incorporate a few different third-party libraries you often end up writing a few different DSLs. Everyone has their own subset of operator overloads that do different things in different contexts and you just have to remember which one does what based on your receiver type -- which is often 'auto' these days anyways. It's maddening. Worse still, the STL plays this game. Time to move on.
There are lots of cases where overriding operators makes sense and is intuitive to the reader, for instance when you are dealing with monoids, groups or rings.
fmt::memory_buffer out1;
std::ostream out2 = std::cout;
auto out3 = printer(stream);
auto out4 = my_printer();
format_to(out1, ...)
out2 << "Hi..."
out3.print(5);
out3 += 6;
These all do the same but it's a total mess.Looks like it's now public as a part of Abseil: https://github.com/abseil/abseil-cpp/blob/bf29470384a101b307...
#include <fmt/ranges.h>
std::vector<int> v = {1, 2, 3};
fmt::print("{}", v); // prints {1, 2, 3}In some ways they are similar, though I felt it was valuable to avoid using ostreams at all, and store indentation levels separately from the printed string. Additionally, I think reflection macros are especially helpful for creating printers for user-defined types.
edit: the similarities are pretty obvious, but for those too lazy to Google, see https://docs.python.org/3/library/pprint.html
Edit: this also lets them assert on argument types.
If you squint enough, it looks like a fluent interface, just fluent interfaces wouldn't become mainstream until later. That is, "cout << a << b << c;" is pretty close to "cout.print(a).print(b).print(c);"
Another reason to use an operator is that if you use free functions (without variadic parameters) then your parens and nesting stacks up: "print(print(print(cout, a), b), c);"
An infix operator doesn't have that problem.
In modern C++, there is at least the possibility to replace "<<" with something better for printing.
My guess: because it has very low precedence, allowing stuff like:
> cout << a + b * c << " is the result\n";
Instead of:
> cout << (a + b * c) << " is the result\n";
So making an object for printing probably helps with scope issues like that.
I was sad that they were spending a bunch of resources on that relatively useless stuff, rather than a sorely needed set of standard collection classes, not to mention a string class. And by "standard" I don't mean actual standards -- it was far too early in the game for that -- but even just enough to get by.
pprint::PrettyPrinter printer;
printer.print("x'x\"x");
And the output is "x'x"x"
So it doesn't actually escape any characters in the string. Same with newlines inside the string.