Hacker News new | ask | show | jobs
by usefulcat 1084 days ago
> I'd like to hear what is as portable and better than iostreams

Have a look at fmtlib. The interface is more like printf, but type safe and format strings are parsed at compile time. I believe it’s what std::format is based on, which could also be an option for you depending on how recent your compiler/language version is.

1 comments

> 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

> 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.

> 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.

It's Bjarne (Stroustrup)'s pet feature. You'll notice that many expert practitioners of C++ write std::println("Hello, world"); or similar for a canonical C++ 23 Hello World, but Bjarne pointedly still writes it with std::cout and the iostream operator overloads.

Now that Bjarne is back at Columbia teaching, there's a risk he'll infect impressionable young people with this nonsense, COMSW4995 was not an intro class and I suspect Bjarne's jetsetting makes it impractical to teach such a class, so this risk is small but not zero.

fmtlib knows the types of all the arguments, and those types are checked against the format string at compile time. So type safety ("%d" vs "%s" etc) is not really an issue.

FWIW, I have waaaay more experience with iostreams than printf. I once wrote a replacement for most parts of std::ostream purely because I had a logging system that was loosely based on log4cxx (which is based on iostreams) and I needed much faster formatting.

Right now I'm in the process of switching to a different logging system that uses fmtlib. I can say that I really, really do not miss the extreme verbosity of iostreams at all. The statefulness I could take or leave, although on balance I'd say it's usually more of a negative. Thankfully I don't have to give up type safety or extensibility.

As for treating files/strings/whatever as streams--my impression of fmtlib is that it's concerned primarily with formatting, which frankly I'm fine with. It can write to an in-memory buffer or to a FILE* or to a std::ostream, which covers pretty much all of my needs. Given the performance of iostreams (lots of virtual method calls) I never really saw the point of using it as a more generalized 'streaming' interface, even knowing that it's possible.

Considering the origins of "stockholm syndrome" this may not be exactly the message you intended.

However, printf and its ilk get it right; presentation is a property of the context in which the entity is to be presented, not of the entity itself. You can kvetch about the markup syntax, or the type safety issues the C implementation has, but the architecture is fundamentally correct.