10

I have been working in C and C++ and when it comes to file handling I get confused. Let me state the things I know.

In C, we use functions:

  • fopen, fclose, fwrite, fread, ftell, fseek, fprintf, fscanf, feof, fileno, fgets, fputs, fgetc, fputc.
  • FILE *fp for file pointer.
  • Modes like r, w, a

I know when to use these functions (Hope I didn't miss anything important).

In C++, we use functions / operators:

  • fstream f
  • f.open, f.close, f>>, f<<, f.seekg, f.seekp, f.tellg, f.tellp, f.read, f.write, f.eof.
  • Modes like ios::in, ios::out, ios::bin , etc...

So is it possible (recommended) to use C compatible file operations in C++? Which is more widely used and why? Is there anything other than these that I should be aware of?

Krypton
  • 3,337
  • 5
  • 32
  • 52
0aslam0
  • 1,797
  • 5
  • 25
  • 42

3 Answers3

14

Sometimes there's existing code expecting one or the other that you need to interact with, which can affect your choice, but in general the C++ versions wouldn't have been introduced if there weren't issues with the C versions that they could fix. Improvements include:

  • RAII semantics, which means e.g. fstreams close the files they manage when they leave scope

  • modal ability to throw exceptions when errors occur, which can make for cleaner code focused on the typical/successful processing (see http://en.cppreference.com/w/cpp/io/basic_ios/exceptions for API function and example)

  • type safety, such that how input and output is performed is implicitly selected using the variable type involved

    • C-style I/O has potential for crashes: e.g. int my_int = 32; printf("%s", my_int);, where %s tells printf to expect a pointer to an ASCIIZ character buffer but my_int appears instead; firstly, the argument passing convention may mean ints are passed differently to const char*s, secondly sizeof int may not equal sizeof const char*, and finally, even if printf extracts 32 as a const char* at best it will just print random garbage from memory address 32 onwards until it coincidentally hits a NUL character - far more likely the process will lack permissions to read some of that memory and the program will crash. Modern C compilers can sometimes validate the format string against the provided arguments, reducing this risk.
  • extensibility for user-defined types (i.e. you can teach streams how to handle your own classes)

  • support for dynamically sizing receiving strings based on the actual input, whereas the C functions tend to need hard-coded maximum buffer sizes and loops in user code to assemble arbitrary sized input

Streams are also sometimes criticised for:

  • verbosity of formatting, particularly "io manipulators" setting width, precision, base, padding, compared to the printf-style format strings

  • a sometimes confusing mix of manipulators that persist their settings across multiple I/O operations and others that are reset after each operation

  • lack of convenience class for RAII pushing/saving and later popping/restoring the manipulator state

  • being slow, as Ben Voigt comments and documents here

Community
  • 1
  • 1
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • Thanks for the quick reply Tony. I didn't understand about the type safety part. In C we have fprintf ,right? Please elaborate about the potential crash. – 0aslam0 Sep 18 '14 at 03:26
  • Please also elaborate on exceptions thrown from iostreams. – Ben Voigt Sep 18 '14 at 03:37
  • 3
    Another criticism of iostreams is that [they are miserably slow](http://stackoverflow.com/q/4340396/103167) – Ben Voigt Sep 18 '14 at 03:38
  • OK.. I couldn't grasp it 100%. But still reading your reply makes me feel better. – 0aslam0 Sep 18 '14 at 03:40
  • 1
    Got it. Thanks again. I understand that C++ file handling is preferred and for legacy system maintenance you have to be aware of C File Handling also. – 0aslam0 Sep 18 '14 at 03:48
  • @Ben Voigt Another reason iostreams are so hideously slow is that: `stream << a << b << c << d << e << f << g << h << i << j;` is ten individual function calls, versus a single call for `fprintf(fp, "format ...", a, b, c, d, e, f, g, h, i, j);` – dgnuff Sep 18 '14 at 04:29
  • @dgnuff: The cost of those 10 function calls is trivial, especially after inlining and other optimization. It's the multiple layers of helper functions and helper classes that iostreams use that kills performance. – Ben Voigt Sep 18 '14 at 04:31
  • @Ben Voigt Exactly - the problem being that you call through that stack of helper functions ten times over. – dgnuff Sep 18 '14 at 04:34
  • 1
    @dgnuff: `fprintf` calls through its helper functions ten times too... they just aren't nearly as overengineered. – Ben Voigt Sep 18 '14 at 04:35
  • Once Stroustrup said `iostream`s are type sensitive, but I've never been able to confirm that. – edmz Sep 18 '14 at 14:13
  • @black: I haven't seen the term formally defined, but when I've seen "type sensitive" used it seemed to refer to polymorphism... clearly iostreams make heavy use of polymorphism, both in overloading of streaming operators and in being able to direct output to any manner of derived stream implementation. I think it's kind of covered by type safety and extensibility in my answer. – Tony Delroy Sep 18 '14 at 14:58
  • @TonyD I could find the [video](https://www.youtube.com/watch?v=Puio5dly9N8#t=1451) and basically he was referring to formatting: you don't have to repeat it. – edmz Sep 18 '14 at 17:13
4

The performance differences between printf()/fwrite style I/O and C++ IO streams formatting are very much implementation dependent.

Some implementations (visual C++ for instance), build their IO streams on top of FILE * objects and this tends to increase the run-time complexity of their implementation. Note, however, that there was no particular constraint to implement the library in this fashion.

In my own opinion, the benefits of C++ I/O are as follows:

  • Type safety.
  • Flexibility of implementation. Code can be written to do specific formatting or input to or from a generic ostream or istream object. The application can then invoke this code with any kind of derived stream object. If the code that I have written and tested against a file now needs to be applied to a socket, a serial port, or some other kind of internal stream, you can create a stream implementation specific to that kind of I/O. Extending the C style I/O in this fashion is not even close to possible.
  • Flexibility in locale settings: the C approach of using a single global locale is, in my opinion, seriously flawed. I have experienced cases where I invoked library code (a DLL) that changed the global locale settings underneath my code and completely messed up my output. A C++ stream allows you to imbue() any locale to a stream object.
TryinHard
  • 4,078
  • 3
  • 28
  • 54
  • Nothing beats knowledge gained from experience. – 0aslam0 Sep 18 '14 at 03:50
  • Although the implementation certainly counts, C++ i/o is required (by spec) go to through a chain of at least four virtual functions (one in stream, one in buffer one in locale and one in a facet) that C i/o is not required to be. This chain of indirections has to be there even if not strictly required by the task, thus making C++ slower than the C i/o needs to be. When they appear to be equal, is because the time spent in transfer the data is parallelized respect to the time spend to convert them in textual form (that's different from C and C++) and slower than both of them – Emilio Garavaglia Sep 20 '14 at 09:07
0

An interesting critical comparison can be found here.

C++ FQA io

Not exactly polite, but makes to think...

Disclaimer

The C++ FQA (that is a critical response to the C++ FAQ) is often considered by the C++ community a "stupid joke issued by a silly guy the even don't understand what C++ is or wants to be"(cit. from the FQA itself).

These kind of argumentation are often used to flame (or escape from) religion battles between C++ believers, Others languages believers or language atheists each in his own humble opinion convinced to be in something superior to the other.

I'm not interested in such battles, I just like to stimulate critical reasoning about the pros and cons argumentation. The C++ FQA -in this sens- has the advantage to place both the FQA and the FAQ one over the other, allowing an immediate comparison. And that the only reason why I referenced it.

Following TonyD comments, below (tanks for them, I makes me clear my intention need a clarification...), it must be noted that the OP is not just discussing the << and >> (I just talk about them in my comments just for brevity) but the entire function-set that makes up the I/O model of C and C++.

With this idea in mind, think also to other "imperative" languages (Java, Python, D ...) and you'll see they are all more conformant to the C model than C++. Sometimes making it even type safe (what the C model is not, and that's its major drawback).

What my point is all about

At the time C++ came along as mainstream (1996 or so) the <iostream.h> library (note the ".h": pre-ISO) was in a language where templates where not yet fully available, and, essentially, no type-safe support for varadic functions (we have to wait until C++11 to get them), but with type-safe overloaded functions.

The idea of oveloading << retuning it's first parameter over and over is -in fact- a way to chain a variable set of arguments using only a binary function, that can be overload in a type-safe manner. That idea extends to whatever "state management function" (like width() or precision()) through manipulators (like setw) appear as a natural consequence. This points -despite of what you may thing to the FQA author- are real facts. And is also a matter of fact that FQA is the only site I found that talks about it.

That said, years later, when the D language was designed starting offering varadic templates, the writef function was added in the D standard library providing a printf-like syntax, but also being perfectly type-safe. (see here)

Nowadays C++11 also have varadic templates ... so the same approach can be putted in place just in the same way.

Moral of the story

Both C++ and C io models appear "outdated" respect to a modern programming style. C retain speed, C++ type safety and a "more flexible abstraction for localization" (but I wonder how many C++ programmers are in the world that are aware of locales and facets...) at a runtime-cost (jut track with a debugger the << of a number, going through stream, buffer locale and facet ... and all the related virtual functions!).

The C model, is also easily extensible to parametric messages (the one the order of the parameters depends on the localization of the text they are in) with format strings like

@1%d @2%i allowing scrpting like "text @2%i text @1%d ..."

The C++ model has no concept of "format string": the parameter order is fixed and itermixed with the text.

But C++11 varadic templates can be used to provide a support that:

  • can offer both compile-time and run-time locale selection
  • can offer both compile-time and run-time parametric order
  • can offer compile-time parameter type safety
  • ... all using a simple format string methodology.

Is it time to standardize a new C++ i/o model ?

JeanLuc
  • 4,783
  • 1
  • 33
  • 47
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • "but makes to think..." - yeah, it shows how someone with reasonable knowledge of but a strong dislike for C++ can write deliberately short-sighted and incomplete FQAs and bask in the "fame" of adoring beginners. – Tony Delroy Sep 18 '14 at 15:14
  • @TonyD: what part of "Not exactly polite" was not clear? It seems to me you are making the opposite error. Many argument about the C++ way of using injection of << operators are common: C++ is the only language that handles IO that way. And the reason is that when iostream was designed C++ din't have templates and (of consequence) varadic templats, that are required to implement type safe printf-like structures. Just give a look to D's `std.write` / `std.read`. They can be ported in C++11 almost trivially, but in C++96 (whatever you want to call the pre-iso one) .... – Emilio Garavaglia Sep 18 '14 at 16:37
  • "what part of "Not exactly polite" was not clear?" - I didn't mention *anything about politeness* - it's the technical arguments that are overwhelmingly pathetic and often deliberately short-sighted. And `cout << a << b` vs. `cout(a, b)` is the subject of about 1% of the FQA content you link - s barely mentioned in 15.4, which reverts to more comfortable ground whinging about the old `operator void*` vs. `bool` issue (resolved in C++11). – Tony Delroy Sep 19 '14 at 02:55
  • Seen your rewrite. If the FQA is the only place you've seen mention `<<` and `>>`, it's because C++ programmers generally like the notation (if not the verbosity of iomanipulators). Python's print offers that notation too. `boost::format` already offers a lot of what you seem to want. Something older isn't necessarily any less ideal today, few languages had operator overloading available when deciding their I/O notation, and given "modern programming style" tends to be used to contrast C++ with the deliberately dumbed-down Java and its C# derivative - who cares how they do it? – Tony Delroy Sep 20 '14 at 04:49
  • " If the FQA is the only place you've seen mention << and >>, it's because C++ programmers generally like the notation". They like it that much that when they have to something other then a space-separated input, start doing other things around it, that the >> operator becoms only an invisible detail, if it 's still there... We have certain different experiences, but when it's time go go "real time", the first thing I throw away is << and go back to sprintf. And read the keyboard using raw API. It took me five years to fully understand the stream-buffer-locale-facet chain... – Emilio Garavaglia Sep 20 '14 at 09:16
  • ... and 1 hour to understand printf. There have to be something in the middle! – Emilio Garavaglia Sep 20 '14 at 09:18