Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

> you have to run a single time to know it works

A common use case for cstdio/iostreams/std::format is logging. It's not at all uncommon to have many, many log statements that are rarely executed because they're mainly for debugging and therefore disabled most of the time. There you go, lots of rarely used and even more rarely 'tested' formatting constructs.

I don't want things to start blowing up just because I enabled some logging, so I'm going to do what I can to let the compiler find as many problems as possible at compile time.



sort by: page size:

> Maybe fmt fixes these problems, I don't know.

Yeah, it looks like you did a lot of guesswork in that comment, and a lot of those guesses were inaccurate. Not really trying to be hostile here, but you did acknowledge that you were unfamiliar with std::format.

The part that fmtlib / std::format has, which is printf-like, is the idea of having a format string and arguments, rather than having a bunch of separate, piecemeal strings.

  // Old printf code, works ok for most people
  std::printf("failed to clone %s from %s", target, src);
  // <iostream>
  std::cout << "failed to clone " << target << " from " << src;
  // New std::format / fmtlib
  std::print("failed to clone {} from {}", target, src);
You can see that you don't need to remember what kind of format specifier you need. This is C++, and that kind of problem is solved with overloading.

The std::print interface can work equally well with FILE or std::ofstream, or whatever you want. This is C++, and so you can just use a templated output iterator—or one of the overloads that creates one automatically.

There are a lot of problems with <iostream>. I think it’s telling that lots of languages have copied printf, but nobody (or almost nobody) thought <iostream> was good enough to copy. There are just too many serious design flaws with <iostream>. It would be one thing if <iostream> were just annoying to use, but it poses problems for localization, thread-safety, accidental misuse through its statefulness, and its operator overloading syntax is bad.


> I'm not saying it's impossible to write a bad printf line and never test it, only to have it fail years later in production. It's absolutely possible and it has happened to me. Lessons learned.

A modern compile time type checked formatter would have prevented this mistake, you are deliberately choosing to use poor tools and calling this "pragmatism" because it sounds better than admitting you're bad at this and you don't even want to improve.

In fact C++ even shipped a pair of functions here. There's a compile time type checked formatter std::format, which almost everybody should use almost always (and which is what std::println calls), and there's also a runtime type checked formatter std::vformat, for those few cases where you absolutely can't know the format string until the last moment. That is a hell of a thing, if you need one of those I have to admit nobody else has one today with equal ergonomics.


> Why is there an input stream going into a function?

The function is constructing a temporary object and reading into that object. The object includes a reference to a named tp object, and the temporary parser object is parsing from the input stream, which writes the data into the tp object.

> but... I still don't understand all of the technical reasons behind that decision.

Because he wants to support input from an istream, and inputting from a istream necessarily means overloading '>>'. That's just how things are done in C++; if you want to support reading from or writing to an istream/ostream, you overload the '>>' and '<<' operators.

It's a trade off; he could have designed it so that the constructor of tp simply accepts the format string and the istream as parameters. But that has the downside of not looking like idiomatic istream code.

I personally think that the way iostreams are implemented in C++ was a mistake. (because I don't like the way idiomatic istream looks, and I think friend functions are generally smelly.) Unfortunately, it's something like 35 years too late to correct it. (I'm aware that C++ is only 33 years old; iostream.h and cout << "blah" << endl; and cin >> foo; preexist C++) But here's the thing: it's badly designed iostreams that make the linked code confusing, not operator overloading.


> trying to understand what the fuck cout<<x actually does

You can always do fprintf instead of that; i almost never do C++ streams for io - there is always a way to avoid them.


"Consider the hoops one needs to jump through to make std::print work with a custom type when compared to the old stream operators."

As an old timer, this kind of made me laugh. I remember when people found the old stream operators burdensome.


> random habit of mine to put std:: in front of these C functions

And did you learn your lesson about making random changes that "shouldn't matter" without proving they don't matter? :)

I find that once I spend the time to make these changes correctly, they are not worth the time to make correctly.


> The second point (how to print an integer) is about something way less easily discoverable than in the previous example. You need to learn about how to print, then about `fmt` and from there the only authoritative place that contains information about format specifiers is the doc comment of `std.fmt.format`.

I don't know if ZSF can guide this or if we can incept it into Andy's head, but if there were any coordinated plan to "solidify certain parts of the stdlib sooner than other parts" the std.fmt.format would be high on my list. And document it. Hell. document it, even if it changes around all the time. I'm often forgetting how that beautiful bastard works and having to jump to the comment in the stdlib is kind of annoying.


> ...what is the use case for a program that operates on arbitrary user defined types?

That's not what's going on. Neither C nor C++ really permits that.

We're just talking about the relative merits of using printf and friends vs. using iostream to format data for the console or disk.

In either case, you have to write the code for your class that massages the class's internals correctly, but the question is, do you write to the ostream extraction and istream insertion API, or do you write to the substantially less well defined printf-and-friends API?


> std::list is an awful container...You should never use it, unless you really know what you are doing

To put a finer point on that: you should use std::list iff you need to splice (or insert into the middle) in constant time.


> there have been several times where I would have to write the same C/C++ function again and again to support a new type.

If the function has to handle different types in different ways, you'd have to write it again anyway. Otherwise, you could have used templates.


> However, there are tricks that compilers cannot use safely.

Why not? VC++ emits cmov quite often, for operator ? and similar code.


> The thing though is that these checks are NOT free and the overhead is not justified for all use cases so forcing them on everyone is not appropriate for C++.

Well, that's why they should be a flag. The question is whether it should be enabled by default or not.


> I honestly don't know how C++ programmers typically handle this situation.

The verbose one. A few extra lines rarely matters. I think the number of times this has come up in my code base is very very small, maybe a few dozen extra lines across hundreds of thousands.


> a feature C manages to have, but here's Rust's

This is clearly disingenuous. C's string formatting is a completely different thing from that Rust snippet or your snide remark about std::format.

I'd rather use printf than cout, I think iostream is clunky, but printf is definitely a footgun & a fairly common source of bugs itself. So much so that compilers had to add support for specifically recognizing printf() calls & validating the inputs.

Also don't forget C still doesn't have standard placeholders for sized types. Gotta use that derpy PRId64 & friends which makes printf() almost as ugly looking as iostreams.


> I spend an annoying amount of time trying to compare/add/multiply... different types in C++ like time, durations, dates, ints, floats in my day job.

Multiplying ints and floats "just works" in C (although you have to mind overflow, but that's not a type issues).

Other examples don't make a lot of sense. E.g. why would you need to multiply time by duration, or compare one with the other? They're different quantities, reflected as such in the type system. That it won't let you do something like that is the whole point - it would be a bug if it did (and if not, then the data types were chosen wrongly).

OTOH, adding a duration to a date is semantically meaningful - which is why std::chrono has operator+ overloaded for various combinations of time_point and duration.


>If I had "using namespace std;" then all of my code is suddenly broken.

If this happened and if you included that specific header, you would get a simple compiler error due to ambiguous methods. Then you can simply add a namespace somewhere and move on. I know sometimes this can involve a lot of changes (if we're talking about other `using namespace ...` usage) but in general you're not gonna have problems.

If you're really worried about this, you can move the statement closer to where you need it. This is very helpful for dense code that you normally want to be verbose.


> C Printf never was, never could be and never will be a suitable way to output data from C++. Now excuse me while I go through the list of thousands of predefined format macros to find out which I need to use to output a uint_fast16_t without making the compiler vomit nonsense.

   printf("%d\n", (int) myfast16_t);
Not that terrible for a type that I've never used, nor seen used.

> The interface is more like printf

See, I don't believe that's an improvement. Having used printf in C, I was relieved to be able to "redirect" whatever to a stream, and not care about whether it should be "%d" or "%02f" or even if it was a struct/class.

On top of this, treating files as streams, strings as streams, or even extending streams to make a tee-stream[0] all seem clunkier to me with a printf like system.

Maybe fmt fixes these problems, I don't know. But I feel a lot of people don't like iostreams because they have some form of Stockholm syndrome with printf.

[0] - https://wordaligned.org/articles/cpp-streambufs#tee-streams


> And, in my entire career, I have never had this problem.

You never used types such as std:: function?

next

Legal | privacy