
Pprint: Pretty Printer for Modern C++ - starbugs
https://github.com/p-ranav/pprint
======
rwj
The library {fmt}
([http://fmtlib.net/latest/index.html](http://fmtlib.net/latest/index.html))
is on track to be included in the standard.

~~~
arcticbull
Wow, this is so much better. I hope they deprecate cout and its inane operator
overloads. Call me old-fashioned but I like an operator to do something at
least logically consistent with its intended use. Shifting shouldn't print or
read from a stream, what on earth made them think that's a good idea?

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.

~~~
0db532a0
Why? I think there’s a reason behind it. I always imagined it to to be
shifting bits along the stream.

~~~
arcticbull
It's about violating the principle of least surprise. If you create a culture
of redefining any and all operators all the time to effectively create DSLs,
you can't infer what _anything_ does. You can even override operator, for some
reason. Just looking at a piece of code you have no idea what it's actually
intending to do.

~~~
0db532a0
I agree with you that there are some DSLs which are very difficult to
understand at first look. However, to me shifting, and pushing/pulling over
streams are not so dissimilar.

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.

~~~
arcticbull
The issue is not that each one in isolation is particularly problematic, the
issue IMO is when they get assembled by an end-user into a pile of mixed
metaphors.

    
    
      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.

~~~
0db532a0
Anyone who writes like that needs to have a chat with Uncle Bob.

~~~
arcticbull
Haha, that's fair :) I'm trying to convey that sometimes as an end developer,
you pick a number of third party libraries based on what they do -- not what
their API definition looks like. Then you end up in an awkward mix-n-match of
DSLs that are hard to reason about without a deep knowledge of each because
the operators you normally key off of are redefined. Maybe thats why I'm not a
C++ developer haha.

------
calebzulawski
I also recently created a pretty printer library
([https://github.com/calebzulawski/hippo](https://github.com/calebzulawski/hippo)).

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.

~~~
phaedrus
I like your idea to support separately tracked indentation levels.

~~~
calebzulawski
Thanks! It really makes writing printers very easy.

------
saagarjha
I find it interesting that this library requires the creation of a "printer"
object instead of the (IMO more standard) definition of operator<<. Is there
any specific reason why the author chose to go this way?

~~~
colejohnson66
Does anyone know why C++ uses the shift operator for streams? It’s always
rubbed me the wrong way

~~~
phaedrus
Originally C++ lacked templates, and even once it got templates it was 20 (?)
years before it got variadic templates. Operator overloads were added early,
however, so using an operator for this was a "hack" to make a type safe print
function. (It probably also served as a demo application for operator
overloading.)

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.

~~~
rand84545
I mean, it's also looks intuitive. if you imagine the arrows as the data
fllowing.

------
kccqzy
This is a bit like '%r' or repr() in Python, the Debug trait in Rust, or the
Show class in Haskell, but it doesn't really address what most pretty printers
need: highly customized formatting, as used in Wadler/Leijen pretty printers.

~~~
tommikaikkonen
Indeed. I wrote a Python pretty printer that uses a Wadler/Leijen algorithm,
and it's a great fit. It doesn't take too long to learn how to use the layout
primitives, and it allows for easy creation of higher level abstractions while
still maintaining plenty of control.

[https://github.com/tommikaikkonen/prettyprinter](https://github.com/tommikaikkonen/prettyprinter)

------
flo123456
There’s also
[https://github.com/tfc/pprintpp](https://github.com/tfc/pprintpp) which is
quite nice.

------
kccqzy

        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.

