2
printf("%d: %d, %d\n", foo, bar, baz);

is much cleaner than

std::cout << foo << ": " << bar << ", " << baz << "\n";

and there is no obvious way at all to rewrite

scanf("%d: %d, %d\n", &foo, &bar, &baz);

other than, say

std::cin >> foo;
std::cin.ignore();
std::cin >> bar;
std::cin.ignore();
std::cin >> baz;
std::cin.ignore();

which is inferior for obvious reasons.

Why aren't there functions like istream::scanf and ostream::printf? I can't see any reason why the following shouldn't have been made possible:

std::cout.printf("%d: %d, %d\n", foo, bar, baz);
std::cin.scanf("%d: %d, %d\n", foo, bar, baz);

I'm sure someone must have proposed it for the standard at some point, and it must have been rejected. Why?

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • 2
    because scanf and printf are inherently unsafe. They use data to find code, which is a good source of fatal bugs. There is a template-based safe analog of printf in the boost library. – Richard Hodges May 04 '14 at 20:53
  • You may wanna read this : http://stackoverflow.com/questions/2872543/printf-vs-cout-in-c – KeatsPeeks May 04 '14 at 20:54
  • Occasionally, I use `template std::istream& operator>> (std::istream& i, char const(&)[N]) { return i.ignore(N); }` for very basic input tasks (maybe plus error checks). You can then write `std::cin >> foo >> ": " >> bar >> ", " >> baz >> "\n";` But still, there's an asymmetry in the operators. – dyp May 04 '14 at 21:02
  • 1
    We have variadic templates now; in the original stream design those functions had to use variable argument lists (with ellipsis and `va_args`). You'd loose the type information (safety) and could not restore the types to allow overloading on custom type (output `MyClassType`). So maybe a part of this question is *why hasn't it been added to C++11*? – dyp May 04 '14 at 21:07
  • Similar to dyp, I've got an `ignore` class which allows me to write `std::cin >> foo >> ignore(": ") >> bar >> ignore(", ") >> baz;` I find the extra verbosity worth it. – MSalters May 05 '14 at 09:02

3 Answers3

3

First, you can always use the C functions from C++.

Second, there are very few "real" programs that use scanf, as you normally need something that gives you more control about what you read, and how to separate it from input stuff you don't want to read. Programs read from files, or maybe some user interface, but seldom from "standard input", and if they do, like the plethora of unix tools, they normally don't use scanf.

With printf, there probably just wasn't any reason to duplicate printf, or sprintf to a string, in C++ classes.

Guntram Blohm
  • 9,667
  • 2
  • 24
  • 31
  • re: reading from files, if `scanf` and `printf` equivalents were implemented in `istream` and `ostream` like I said, they would be available for reading from and writing to files too. – Brian Bi May 04 '14 at 20:59
2

You can still use printf and scanf in C++. So, what would be the benefit of your proposal? You gain nothing by just giving them different names.

They are not very C++-style functions, mostly because they are not type-safe and they are prone to runtime error that cannot be avoided by compiler checks.

The C++ analogue to these functions is stringstream.

tenfour
  • 36,141
  • 15
  • 83
  • 142
  • For the standard streams, you can use `scanf` and `printf` if you want. For `fstream`s, you can't. It would seem silly to open a file twice, as both a C file and a C++ `fstream`, just so you can use `scanf` and `printf` on it in addition to C++ stream operations. – Brian Bi May 04 '14 at 20:58
  • 1
    I see what you mean, but these are really dirty functions. If you understand how un-C++ they are, then it should be clear to see why designers have been reluctant to continue the format specifier idiom in C++. There have been C++ alternatives which are more type-safe, like `Boost::Format`, but I wouldn't say it's universally accepted. What makes `printf` attractive is how little code it is. **This is just not the appeal of good C++ design**. – tenfour May 04 '14 at 21:08
  • Yes, you can still use the C functions in C++, but they won't give you C++ `std::string`s. `fprintf()` is extremely useful both for formatting stuff for output and for generating strings via `open_memstream()`. The later produces a perfectly allocated C string for you, but not a C++ `std::string`. In this respect, the C++ system is truly inferior to the C system, and its not really interoperable. – cmaster - reinstate monica May 05 '14 at 03:41
2

Type safety, and using data to determine program flow in an uncheckable manner.

Prior to C++11 there was no type safe variardic construct in C++. So the variardic printf syntax was barred. Now you could get close:

cpprintf("bob %s your %s! %d\n")<< "is" << "uncle" << 42;

where we create a formatter object then << arguments into it.

The downside here is that the string "bob... is controlling code flow -- data controlling code was a serious source of errors and exploits.

When user defined constexpr literals arrive we will finally be able to 'fix' this, so the format string can be parsed at compile time and the arguments checked for type safety.

Oh and the last problem is printf does not allow objects to format themselves -- it is not extendable by printf users.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • *"the arfuments checked for type safety."* Typo. Anyway, you wouldn't need that information, it's redundant (I'm not sure why people cling to that C `printf` syntax): `print("% is the %, %\n", 42, "solution", "Arthur");` The formatting string allows, for better and worse, simple dynamic formatting. Btw: What's the problem with *user-defined constexpr literals*? Can't a UD literal operator be `constexpr`? Or do you want metaprogramming on the contents of the string? – dyp May 05 '14 at 17:00
  • @dyp I actually want more than `constexpr` (I worded that ... wrong) -- I want the string as the contents of a type, so `"%d -- %d"` is of type *'eats two integers'*, not *'eats some parameters and stuffs it if there is room'*. The `` style syntax, which I believe was removed, is what I'm talking about. – Yakk - Adam Nevraumont May 05 '14 at 17:20
  • I think I meant that too :) *"metaprogramming on the contents"* we can already do compile-time string parsing, but using this to compute a type is impractical because of the invocation mechanism required (either exploding the string via a macro, or putting it inside a local type as a static member function etc.) The `` syntax unfortunately is only allowed for integers/floating point types, but not for strings :( – dyp May 05 '14 at 18:01
  • @dyp as a work around, use hex. Each code point is encoded as two or more digits. The leading digit encodes the subtype of the following sequence. So we get wonderful stuff like `0x1d010C01480165016C016C016F01200157016F0172016C016401211d_printf(3,7)` meaning `printf("%dHello world!%d",3,7)` -- typesafe, and easy to read! :p – Yakk - Adam Nevraumont May 05 '14 at 18:14
  • 1
    Oooh that will be *much* cleaner in C++1y -- we can finally have digit group separators: `0B00011101'00011010010010000110010101101100011011000110111101010111011011110111001001101100011001000010000100000000'00011101_printf(3,7)` the s<->a is a typo ;) – dyp May 05 '14 at 19:02