One Does not Simply Rename Into C++

Programming is in many ways more art than science—I do not want to start that debate in this post—in that you need more than mere functionality and correctness to have great code. For code to be great, it has, amongst other things, to be beautiful in that strange, vague, language-specific way.

As you know, this blog is C and C++-centric. Those are the two main languages I use both for personal and for professional projects. I resisted the transition from Pascal to C a long time, for many reasons. One was that at that time C compilers were flimsy, while we had a couple of really great Pascal compiler, such as Turbo Pascal—quite the upgrade from my Apple II’s USCD Pascal. Another was that I found C just ugly, clunky, and primitive; it was terse and inelegant. But over the years, I learnt to like the way C gives you pretty good control on what code is generated—not that you can predict right down to the assembly instructions what the compiler will generate; but you still have a very good idea if you understand even vaguely the underlying machine.

C++ was the next transition, and it was a rather difficult one. The first compilers based on cfront were simply dreadful. Then again, combining C’s simplicité volontaire with the already familiar paradigm of object orientation (that I learnt from Turbo Pascal, mainly) truly opened new horizons.

For a very long time C was the undisputed standard in many areas, notably embedded and real-time/high-performance programming. Some of the reasons are just stupid, but enduring, myths, sometimes propagated by individuals enjoying high visibility. Of course, if you tried C++ one week-end in 1994 with a borked compiler, you may get a very bad impression of the language. Now, as of today, C++ compilers are still not all equals (some are more equal than others) and we can say that it is quite possible to use C++ without worrying (too much) about the compiler. Needless to say, myths, however unfounded, are really hard to kill. Eventually, more and more software projects switch to C++ in the hopes of getting the boon of object orientation and sophisticated template meta-programming that C lacks.

The problem is the transition code. It seems that many C programmers think it suffices to rename a C file with C++ extension to program in C++! Isn’t C++ a super-set of C ? No, it is not. And there’s the question of style.

When I learn a new programming language, say, Python, I will try to get the spirit of the language rather than just replicate what I already do in other programming languages. I often pay a visit to the #python channel on freenode.net (that’s a IRC network) to ask the real pythonistas what’s the most pythonèsque way of doing such and such. Likewise in C++, I often seek the counsel of friends that know a lot more about C++ than I do.

The same goes with C and C++. While some C code may be perfectly cromulent C++ code, it is just still C. To do C++, you need to do a bit more than merely renaming the file’s extension to .cpp (or the like). One thing is to stop using the stupid string functions (such as strcpy and just use the standard library std::string class. One other is to stop using the archaic file functions, like fopen, fread, printf, and sprintf and start using std::fstream. Not only are they harder to use correctly than the C++ stream equivalent; they are inherently less safe.

Yes, yes they are. printf is quite much harder to use correctly than the C++ equivalent streams. First difficulty, getting print format strings to be type-safe. To use printf (and all its friends) type-safely, you have to resort to C99’s standard headers <stdint.h> and <inttypes.h>. For example:

 #include <stdint.h>
 #include <inttypes.h>

 uint64_t x; /* safe */

 printf("%" PRIu64 "\n",x); /* also safe */

While using C++’s standard streams:

 std::cout << x << std::endl;

will work properly regardless of what basic type x is (as long as it has a friend operator<<). Another troublesome sibling is sprintf (and its variant snprintf). sprintf sends its printf-formatted output to a string buffer that must have been previously allocated. It is not rare to find code that looks like:

 char buff[NEVER_ENOUGH];

 sprintf(buff,"%s\n",begging_for_problems);

of course, that’s really dangerous programming. Already:

 char buff[NEVER_ENOUGH];

 snprintf(buff,NEVER_ENOUGH,"%s\n",begging_for_problems);

is much better as it will output at most NEVER_ENOUGH characters (including the terminating \0). While it averts the dangerous buffer overflows, it may also truncate the output. Managing arbitrary input lengths (but with within the capabilities of the machine) requires quite a lot of work in C. In C++, creating a std::ostringstream pretty much solves the problem:

 std::ostringstream s;

 s << begging_for_problems;

 if (s.bad())
  {
   ...deal with error
  }

(Systemfault suggested using s.exceptions(std::ios_base::bad_bits) to cause the stream s to throw an exception rather than merely turning bad. The code then uses another construct unfamiliar to a lot of C programmers: exceptions! The std::string hidden inside the std::ostringstream may throw std::bad_alloc in some implementations of the standard library.)

But I digress. My point is not so much that C++ is much better than C (because that is quite debatable, in the same absurd way one could argue that the screwdriver is better than the hammer) but that using language-specific and language-friendly constructs leads to more beautiful code, and quite often more robust code; even faster code. In Python’s case, carefully choosing your constructs will often give you much faster code—and not necessarily for obvious reasons.

*
* *

Aestheticism in programming is as elusive as in anything else; of course, and what one may find beautiful another may find merely kitsch. We may not even agree on what is just better; and certainly not about the finer points. I would think one would agree that using std::ostringstream is better than using sprintf if the former is available; yet I’m sure one could make a very compelling argument for the latter (and I’d be curious to ear about it). But, as I said before I’m sure, every time you have the choice between two constructs, the one with the lesser chance of error is the better. Does it lead to more aesthetically pleasing code? Maybe.

*
* *

The bottom line remains that when in Rome, do like the Romans. While in transition periods it may be reassuring to use constructs from another programming language in the new language, after the initial contact one must eventually ‘tune in’ and embrace the coding style the language is made for. C++ is a good example of this; one must eventually give up on the ways things are done in C. Another good example is Python, where merely transposing procedural programming leads to suboptimal results. List comprehensions, for example, are much faster than their procedural equivalents—and simpler and safer in many cases!

3 Responses to One Does not Simply Rename Into C++

  1. rmn says:

    While I agree stringstreams in C++ are much better (in ease of use, type safety, etc) than snprintf and the likes, they also come with a small efficiency lose. I’d suggest reading litb’s comment on http://stackoverflow.com/questions/445315/why-is-snprintf-faster-than-ostringstream-or-is-it for example, and I also recall some discussions over this in the Boost mailing lists.

    I couldn’t agree more with your post though; When in Rome..

  2. Steven Pigeon says:

    Performance may be a problem in some cases, true, but then you would probably not use printf-like function to do output, but a binary format of some sort.

    In all cases, correctness > speed.

  3. […] Learning a new language, even when strangely close to the paradigms you’re used to, will inevitably cause you some surprises and the adaptation may or may not be easy. It’s not that I’m just learning Python (I’ve done some fairly complex things with it before) it is just that I am trying to write proper Python code, not just C++ in Python. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: