C++ Streams VS. C-Style Io

C++ Streams vs. C-style IO?

This is an heated topic.

Some people prefer to use the C++ IO since they are type-safe (you can't have divergence between the type of the object and the type specified in the format string), and flow more naturally with the rest of the C++ way of coding.

However, there is also arguments for C IO functions (my personal favorites). Some of them are:

  • They integrate more easily with localisation, as the whole string to localise is not broken up in smaller strings, and with some implementation the localizer can reorder the order of the inserted value, move them in the string, ...
  • You can directly see the format of the text that will be written (this can be really hard with stream operators).
  • As there is no inlining, and only one instance of the printf function, the generated code is smaller (this can be important in embedded environment).
  • Faster than C++ function in some implementation.

Personally, I wouldn't consider it bad practice to use C stream in C++ code. Some organisations even recommend to use them over C++ stream. What I would consider bad style is to use both in the same project. Consistency is the key here I think.


As other have noted, in a relatively large project, you would probably not use them directly, but you would use a set of wrapper function (or classes), that would best fit your coding standard, and your needs (localisation, type safety, ...). You can use one or the other IO interface to implement this higher level interface, but you'll probably only use one.


Edit: adding some information about the advantage of printf formatting function family relating to the localisation. Please note that those information are only valid for some implementation.

You can use %m$ instead of % to reference parameter by index instead of referencing them sequentially. This can be used to reorder values in the formatted string. The following program will write Hello World! on the standard output.

#include <stdio.h>
int main() {
printf("%2$s %1$s\n", "World!", "Hello");
return 0;
}

Consider translating this C++ code:

if (nb_files_deleted == 1)
stream << "One file ";
else
stream << nb_file_deleted << " files ";
stream << removed from directory \"" << directory << "\"\n";

This can be really hard. With printf (and a library like gettext to handle the localization), the code is not mixed with the string. We can thus pass the string to the localization team, and won't have to update the code if there are special case in some language (in some language, if count of object is 0, you use a plural form, in other language, there are three forms one for singular, one when there is two object and a plural form, ...).

printf (ngettext ("One file removed from directory \"%2$s\"",
"%1$d files removed from directory \"%2$s\"",
n),
n, dir);

Performance Difference Between C and C++ Style File IO

You're using endl to print a newline. That is the problem here, as it does more than just printing a newline — endl also flushes the buffer which is an expensive operation (if you do that in each iteration).

Use \n if you mean so:

file << i << '\n';

And also, must compile your code in release mode (i.e turn on the optimizations).

C style, C++ streams or Win32 API File I/O?

To take a broader look, direct use of Win32 is good if you need a tiny application with no additional dependencies.

For anything that C++ iostreams does better, you probably want to look at Boost::Spirit. Seems like it has all the type-safety of iostreams, with much better performance.

You really have two problems here: File I/O, and Text Processing. Win32 does the first exceptionally well, and provides no help with the second. Boost::Spirit does the second very well. C++ iostreams are marginal at both tasks, avoid them unless portability is the most important feature.

should I mix C & C++ style I/O in my C++ program?

C++ and C streams are synchronized by default, so you can mix them safely. This behavior is controlled by std::ios_base::sync_with_stdio.


As to whether you should do it? Doesn't matter. C++ does not have a universal style guide. Some programmers prefer the C++ iostreams interface, some prefer C's methods, some mix them.

Here are some links that discuss the problem better than I can.

Should I switch to C++ I/O streams?

The Duct Tape Programmer - Joel Spolsky

Are the global C++ I/O objects equivalent or using the C I/O streams?

When you say "C" streams, these are really the standard streams delivered to every process by the Operating System.

When a process is created the Operating System creates several low level "file descriptors" that enable input and output to it.

How these underlying standard input/output streams are implemented and in what language is down to the operating system. They have existed in operating systems since before the C language was written.

Obviously "C" provides access to those through <stdio.h> and C++ provides access to them through <iostream>.

I think that to say the C++ library uses the "C" streams may be a little misleading. If we are talking about the Standard C Library then it is unlikely that C++ will utilize those (but it is required to cooperate with them).

The underlying standard input/output streams are not part of Standard C, but they do have a long history with the C language because C was created specifically for writing Operating Systems and so the low level core of Process I/O is likely to be a C library (although it could also be assembler or another language entirely).

For example on POSIX systems there are C library headers for accessing the low level standard input/output streams that are not part of Standard C. This maybe why they are referred to as C streams in your linked documentation however the concept of standard io streams predates the C language itself.

What Standard C and Standard C++ streams do is add layers of abstraction on the raw primitives provided by the Operating System. This is generally formatting and converting between numbers and strings, character encodings , etc. C and C++ do those things rather differently.

C++ iostream vs. C stdio performance/overhead

What's causing a significant difference in performance is a significant difference in the overall functionality.

I will do my best to compare both of your seemingly equivalent approaches in details.

In C:

Looping

  • Read characters until a newline or end-of-file is detected or max length (1024) is reached
  • Tokenize looking for the hardcoded white-space delimiter
  • Parse into double without any questions

In C++:

Looping

  • Read characters until one of the default delimiters is detected. This isn't limiting the detection to your actual data pattern. It will check for more delimiters just in case. Overhead everywhere.
  • Once it found a delimiter, it will try to parse the accumulated string gracefully. It won't assume a pattern in your data. For example, if there is 800 consecutive numeric characters and isn't a good candidate for the type anymore, it must be able to detect that possibility by itself, so it adds some overhead for that.

One way to improve performance that I'd suggest is very near of what Peter said in above comments. Use getline inside operator>> so you can tell about your data. Something like this should be able to give some of your speed back, thought it's somehow like C-ing a part of your code back:

istream &operator>>(istream &in, point &p) {
char bufX[10], bufY[10];
in.getline(bufX, sizeof(bufX), ' ');
in.getline(bufY, sizeof(bufY), '\n');
p.x = atof(bufX);
p.y = atof(bufY);
return in;
}

Hope it's helpful.

Edit: applied nneonneo's comment

Who architected / designed C++'s IOStreams, and would it still be considered well-designed by today's standards?

Several ill-conceived ideas found their way into the standard: auto_ptr, vector<bool>, valarray and export, just to name a few. So I wouldn't take the presence of IOStreams necessarily as a sign of quality design.

IOStreams have a checkered history. They are actually a reworking of an earlier streams library, but were authored at a time when many of today's C++ idioms didn't exist, so the designers didn't have the benefit of hindsight. One issue that only became apparent over time was that it is almost impossible to implement IOStreams as efficiently as C's stdio, due to the copious use of virtual functions and forwarding to internal buffer objects at even the finest granularity, and also thanks to some inscrutable strangeness in the way locales are defined and implemented. My memory of this is quite fuzzy, I'll admit; I remember it being the subject of intense debate some years ago, on comp.lang.c++.moderated.

C++ and C file I/O

Opinion

I don't know of any real project that uses C++ streams. They are too slow and difficult to use. There are several newer libraries like FastFormat and the Boost version that claim to be better there was a piece in the last ACCU Overload magazine about them. Personally I have used the c FILE library for the last 15 years or so in C++ and I can see no reason yet to change.

Speed

Here is small test program (I knock together quickly) to show the basic speed problem:

#include <stdio.h>
#include <time.h>

#include<iostream>
#include<fstream>

using namespace std;

int main( int argc, const char* argv[] )
{
const int max = 1000000;
const char* teststr = "example";

int start = time(0);
FILE* file = fopen( "example1", "w" );
for( int i = 0; i < max; i++ )
{
fprintf( file, "%s:%d\n", teststr, i );
}
fclose( file );
int end = time(0);

printf( "C FILE: %ds\n", end-start );

start = time(0);
ofstream outdata;
outdata.open("example2.dat");
for( int i = 0; i < max; i++ )
{
outdata << teststr << ":" << i << endl;
}
outdata.close();
end = time(0);

printf( "C++ Streams: %ds\n", end-start );

return 0;
}

And the results on my PC:

C FILE: 5s
C++ Streams: 260s

Process returned 0 (0x0) execution time : 265.282 s
Press any key to continue.

As we can see just this simple example is 52x slower. I hope that there are ways to make it faster!

NOTE: changing endl to '\n' in my example improved C++ streams making it only 3x slower than the FILE* streams (thanks jalf) there may be ways to make it faster.

Difficulty to use

I can't argue that printf() is not terse but it is more flexible (IMO) and simpler to understand, once you get past the initial WTF for the macro codes.

double pi = 3.14285714;

cout << "pi = " << setprecision(5) << pi << '\n';
printf( "%.5f\n", pi );

cout << "pi = " << fixed << showpos << setprecision(3) << pi << '\n';
printf( "%+.3f\n", pi );

cout << "pi = " << scientific << noshowpos << pi<< '\n';
printf( "%e\n", pi );

The Question

Yes, may be there is need of a better C++ library, may be FastFormat is that library, only time will tell.

dave



Related Topics



Leave a reply



Submit