10
votes

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?

3

3 Answers

14
votes

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

4
votes

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

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 ?