#CPPCON2017. Day 1. Hope to get something-better-than-chevron-hell

 
Author:  Follow: TwitterFacebook
Job Title:Sarcastic Architect
Hobbies:Thinking Aloud, Arguing with Managers, Annoying HRs,
Calling a Spade a Spade, Keeping Tongue in Cheek
 
 

At CPPCON2017 day 1, interesting (and IMO good <smile />) things were happening.

A Chance to Get Readable Formatting: {fmt}

– Maybe there’s still hope?
– Nope

— Garfield the Cat —

IMO, the most important thing which has happened on Day 1 was a series of discussions about a potential to adding an alternative to overloaded operator << in iostreams <yay! />.

Those who know me for long enough, probably know that I am one of those people who is guilty of considering printf() (with all its type safety problems – though they can be alleviated by Lint-ing) being a lesser evil than cout’s operator << (that is, for text formatting purposes; I don’t have much problems using << for serialization etc.). Fortunately, for quite some time there is a solution which is both type-safe and readable (and also is extensible, translatable, stateless, faster-than-<<, you-name-it).

On Day 1 of CPPCON2017, there were two things which were related to this problem (which I consider one of those having the largest annoyance/work-needed-to-fix ratio). First, there was a talk by the author of the {fmt} (previously cppformat) library, Victor Zverovich:

Chevron Hell

Side note: BTW, I fully agree with the name “Chevron Hell” – along with LOTS of the developers out there (from what I’ve seen – 80%, and IMNSHO would be even more if not for each and every book on C++ pushing << operator down our throats).

Most importantly – the {fmt} library (and recent P0645R0 proposal) by Victor Zverovich solves all the problems of both printf() and cout <<. In particular, it is (a) type-safe, (b) readable, (c) translatable, and (d) extensible. As the icing on the cake, it is also faster than cout << (and just barely slower than printf()):

{fmt} vs printf() vs cout << performance-wise

The second thing related to this proposal was that, during a late-night “Grill the Committee” session,

Grill the Committee

(BTW, here is your chance to see people who are at the same time most-revered and most-hated in the C++ world <wink />1)

I asked the question about better opportunities to the << operator, and the answer was like “IF there is a better option, we would consider it”. Hey, there IS a formal proposal P0645R0 (which was already discussed and encouraged to continue on Toronto meeting, and IMNSHO it would be really nice if at least one of those 15 people present on the stage would read the proposal before implying such a thing is impossible; this is not to mention that the library is out there for like 5 years now). Still, the answer does open the potential to fix this long-standing-and-very-annoying issue not just by using a 3rd-party library (which lots of us, including myself, are already doing) – but also at standard level.

Just in case if some of WG21 committee members are reading this <wink />, here are the advantages of the {fmt} over cout << (again, for text formatting purposes, which include both end-user-oriented-strings and logging):

  • {fmt} is much better readable. While I know that Bjarne2 disagrees with 80% of the industry on the readability of cout << being poor (and while readability is indeed subjective), let’s just compare two pieces of code below.
boost::io::ios_flags_saver ifs(x);
std::cout << "The value is " << std::setprecision(2) 
          << std::fixed << 1.23456 << "\n";

and

fmt::print("The value is {:02f}\n", 1.23456 );

Not sure about Bjarne, but I will take the second version over the first one every day of the weekNB: in the first example, the idea of  boost::io:ios_flags_saver is to save-in-constructor-and-restore-in-destructor those sticky formatting flags; even if it is not really necessary for the flags I’m currently using because they happen to be not sticky, I really hate to think which of the flags are sticky and which aren’t (and whether I am changing a non-sticky one to a sticky one), so I have to use ios_flags_saver pretty much all the time.

  • {fmt} is much better translatable (which is important for end-user-oriented formatting). I am not going to go into a lengthy discussion on this one – but I was involved in i18n into 20+ languages, and can say that all the translations always go by whole phrases (and never ever by those-chunks-between-cout-chevrones). This, in turn, makes i18n of the <<-based code a nightmare (while it is fairly straightforward for printf()- and {fmt}-based stuff – we just need to replace the format string, that’s it).
    • As one of the closely related benefits – positioned arguments are also perfectly working in {fmt} (as “{0}”, “{1}”, etc.)
  • {fmt} works seamlessly both over iostream and over FILE* (and adding new stream to the mix is really simple too)
  • {fmt} is substantially faster than cout << (in experiments – like 2x, which is not to be ignored unless we’re are really big fans of premature pessimisation).
  • {fmt} supports named arguments such as “{value}” (though it seems not to be a part of the proposal).

As for the undisputable benefits of cout-over-printf() – the same benefits exist for {fmt} too:

  • pretty much like operator <<, {fmt} is type-safe. If you specify an impossible format (such as “{:d}” for a string) – you’ll get an exception, not memory corruption <phew />
  • pretty much like operator <<, {fmt} is easily extensible. Adding formatting for your own class to {fmt} is certainly doable and is pretty easy too.

BTW, actually, I am not really insisting on the {fmt} (and even less – on its current implementation): using the line of argument by WG21 members, “if you give me a library which is type-safe, readable, and translatable, I will be happy”; it is just that << fails on 2 counts out of three mentioned, and {fmt} happens to comply with all these 3 requirements (and is extensible too).

Let’s just hope that this small-but-really-annoying issue won’t get buried under WG21 paperwork (and that Victor Zverovich will have enough energy to push it through; good luck, Victor – you’ll need it but we’re keeping our fingers crossed for you).

1 this is certainly not the whole WG21, but still a rather influential part of it. First three people on the left: Herb Sutter, Gor Nishanov, Bjarne Stroustrup
2 This is how he introduced himself during “Grill the committee” session: “I’m Bjarne”.

 

Other stuff of Day 1

Of course, I wasn’t able to attend all the sessions (lots of them were in parallel); out of those I was able of attend – the following one was of most interest:

Learning and Teaching Modern C++

an (as always, brilliant) presentation by Bjarne Stroustrup, on “Learning and Teaching Modern C++”. When it becomes available on Youtube, you really should take time to listen to it even if you’re NOT teaching C++. Among the most interesting and not-so-obvious things mentioned were that:

  • at least trying to understand how-hardware-works IS important
  • “DON’T BE TOO CLEVER!” “‘Clever’ is a swear word”

Still, however audacious it is, here my two (rather minor) points of disagreement:

  • I don’t really feel that “how cool it is” should be taken into account (unless, of course, we’re speaking about increasing course attendance). Otherwise – and as we see in practice – there are way too many developers who’re all about “cool” things (with “cool” very often leading to “being too clever” <sad-face />).
  • Also, I am not that keen of the very idea of “teaching C++ as such”; rather – I’d strongly prefer somebody to start “teaching C++ to do this kind of tasks”. After all, however sacrilegious it sounds on a conference-dedicated-to-C++, programming language is inherently secondary to our task as developers (and it is the task itself which is primary).

Tired hare:Phew, it was a long day – and there is another one coming. I hope to continue my reports from CPPCON2017 tomorrow.

Join our mailing list:

Comments

  1. Jon says

    This is a little disingenuous — Bjarne’s response was basically “I know that cout and << are bad, but printf is even worse and not type checked at compile time. So we need something better but I haven't seen the right proposal yet"

    • "No Bugs" Hare says

      Well, to some extent – yes, but what am I saying above is that “the answer was like “IF there is a better option, we would consider it”” – what’s the big difference with your version? Also – I clearly remember wording along the lines of “some people are insisting on 70s solution with no type checking” (as well as post-session discussion disagreeing that << is unreadable) - which IMNSHO does imply a false dichotomy of "those who don't like cout, push us back to the dark age of printf()" (and it took me some persuasion after the session to convince that a thing which-is-both-type-safe-and-readable is doable). OTOH, this is not the real point; the real point is that we CAN do it better (and with a very little cost), so it SHOULD be done ASAP. BTW, I am perfectly ready to withdraw all the criticism of the WG21 in this regard on the very day {fmt} is voted into the standard 😉 .

  2. mr. iostream says

    “I really hate to think which of the flags are sticky and which aren’t (and whether I am changing a non-sticky one to a sticky one), so I have to use ios_flags_saver pretty much all the time” – AFAIK the only non-sticky flag is width, so there’s not too much to think of. 😉

    • "No Bugs" Hare says

      In practice, you’re right (though I’m not sure whether it is codified in the standard or not), but it doesn’t really change the point: saving-n-restoring ios_flags is required each and every time :-). This is actually the only thing we should be concerned about (but with {fmt}, we shouldn’t, and it _is_ a significant improvement).

  3. MJanes says

    I just wanted to point out that not all iostream users are biased towards text formatting; some of us uses iostream ( either directly on indirectly via libs like, say, boost serialization and many other ) as a means to obtain ‘universal’ string representation of objects ( for serialization, inspection, … ). In this scenario iostream operators are pretty good as far as readability, conciseness and genericity are concerned and hence superior than formatting biased solutions ( performance issues aside ). Of course, having a decent formatting oriented library in the std would be great, but I don’t think of it as a replacement of iostreams, rather than as a natural companion …

    • "No Bugs" Hare says

      Fair enough. Let’s say it should be “potential replacement for iostreams for text formatting purposes” :-). I updated the post to reflect this distinction (at least for me, “text formatting” includes _both_ end-user-oriented strings, _and_ logging/tracing).

      BTW, {fmt} can work on top of iostreams (just replacing formatting), so it is certainly not positioned as “let’s throw iostreams away”; rather, it is “whenever I’m doing logging or user string formatting – I certainly do want something better than < <" (and with amounts of tracing/logging involved in _any_ serious server-side system - it is a Damn Big Deal(tm)).

      Heck – these days even Java got phrase-oriented formatting String.format() (while for a loooong while it was same-style-as-iostream-<<-but-using-+-instead-of-<<), leaving C++ as the only major language lacking this kind of logging/formatting (if we discount printf() as type-unsafe).

      • MJanes says

        I totally agree with you as far as formatting is concerned.
        Regarding logging/tracing, the problem of separating objects from their final string presentation ( via a “format” string ) is the lack of genericity. Consider printing a map of vectors to sets of …. of T’s: in iostream you just need the corresponding generic stream ops defined and that’s it …

        • "No Bugs" Hare says

          > Consider printing a map of vectors to sets of …. of T’s

          TBH, with all the years I never seen the need of it in production logging (during debug – well, maybe, but even there it is very rare). Usually it is more like map of X where X contains map of vectors of Ys, where Y contains sets of Ts – and if we want to log it for whatever-reason – we have to have logging at all of X, Y, and T levels to keep the log human-readable (and production logs _have_ to be human-readable, otherwise they become useless). With debug – situation is indeed different (as we already know what we’re looking for), but for production logging having very-very-easy-to-read logging is the key, which makes < <-style stuff pretty much useless.

          That being said – my understanding is that IF it is desired – nothing prevents us from having the same thing with {fmt} (at least if we have it as a part of the standard, we can have custom formatting defined for all the containers); whether automated fallback of {fmt} to << if no specific {fmt} formatting exists can be implemented - is an interesting question I'll ask Victor (at least, it would indeed be useful for debugging). Overall - having formatting string on_the_top_level doesn't prevent from having silent fallbacks to <<-serialization-style IF human-readable {fmt}-style formatting is not specified. Do we have a deal 😉 ?

    • "No Bugs" Hare says

      TBH, pros-vs-cons of such direct use of variable names in format string are not really obvious to me. While on the one hand it is a bit less error-prone if we’re dealing with format-strings-confined-to-the-code, OTOH for translatable strings it exposes internal structure of the program into the outside world way too much (for example, with this technique, if we’re changing name of the variable involved in format string – it leads to a change of the format string, which, at least when using traditional approaches to i18n, will lead to re-translation of the string into all the i18n languages for no reason).

      BTW, {fmt}-provided named arguments (along the lines of

      fmt::print(“Hello, {name}! The answer is {number}. Goodbye, {name}.”, “name”_a=”World”, “number”_a=42);

      ) avoid this latter problem (though at the cost of additional verbosity 🙁 ).

      What is clear though – is that with _any_of_these_three_approaches_ (positional arguments “{0}”, C#-style ‘interpolated strings’, and {fmt}-style named arguments) – _all_ of them are like _orders_of_magnitude_ better than existing “chevron hell” whenever we’re speaking about formatting of human-readable strings. As {fmt} does already exist as a pure library (and ‘interpolated strings’ will likely require support from the compiler), and as any differences between these three approaches are arguable at least given our current knowledge – I’d certainly prefer to have {fmt} in 2020 rather than whatever-else in 2030.

      Just IMO though.

  4. says

    /*
    2018 JAN — dbj.org created
    */
    namespace dbj {
    inline auto print
    = [](auto && … param)
    {
    if constexpr (sizeof…(param) > 0) {
    // C++17 fold-ing
    (std::cout << … << param);
    }
    };
    } // dbj

    Probably can not work with iomanip, but works for me in 90% + use-cases …

    • "No Bugs" Hare says

      Interesting, thanks!

      However, FWIW, in a later article published in Overload (https://accu.org/index.php/journals/2486) I listed 5 separate flaws of iostream in the context of human-readable outputs, and while your approach does solve two of them, such problems as i18n-being-unmanageable and formatting-in-presence-of-specific-formatting-being-outright-ugly, still seem to exist (while they are solved by {fmt}).

Leave a Reply to MJanes Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.