but printf is inarguably a better string manipulation idiom than stringstreams.
Not when it causes a crash, or invalid output...
Not something like ocaml's printf (and hence f#'s), or things that add similar(arguably more powerful) stuff to haskell *are* better IMO. But really they are all very complex under the hood
One can argue that, now tooling has improved in some languages you can avoid the most common issues in the simple (the format string is a plain literal) case that the actual impact is lower of course. But when r# was installed it found an awful lot of 'wrong number of arguments' bugs in our code base that were festering in rarely used code paths, so I can't argue that without it it should be fine.
I have no problem with formatted string output as implemented in some of the higher-level languages, and they're my preferred method (to do what I'd do with stringstream or printf in C++) in Java or Python, because they're typesafe and more aware of things like number of arguments and argument size. Boost.Format isn't bad either, but, in order to get away from the caveats that come along with printf, its syntax is pretty much awful (they overload the % operator!).
C/C++ printf, on the other hand is fundamentally broken. In the event of a fairly common programmer's error (mismatching the number or types of arguments with the format string), printf can fail in one of three ways:
1) You get invalid output.
2) Your program crashes at the printf statement.
3) Your program crashes at some later point, because printf popped too many/too few arguments off the stack. There is a good chance that this case is exploitable and a security bug.
Yes, most modern compilers and any decent static analysis tools will typically catch this at compile time, but they shouldn't have to-- because you have better tools at your disposal in C++ that don't come along with these caveats.
(And that's not even mentioning the "dynamic or user-generated format string" thing that some people like to pull. Which you should never
do. Because, unless you're really
careful, that leaves the above three cases up to the complete control of an external attacker.)