C/C++ Contributing Editors


Standard C/C++: Frequently Answered Questions

P. J. Plauger

There's a difference between data, information, knowledge, and wisdom. Nowhere is that more apparent than when the same question yields a broad spectrum of answers.


One of the chores I impose on myself is reading the newsgroups. I don't much care for the process — too many flames, misunderstandings, endless discussions, and just plain tedious messages to paw through every day. But I recognize that the newsgroups are an important forum, in my chosen profession, for the dissemination of information and the formation of shared meaning.

The number of newsgroup participants in any given month is clearly a tiny minority of the programming community. Even if you count all the silent lurkers, plus the friends they keep up to date on newsgroup happenings, you're still looking at a drop in the ocean of programmers out there. But that tiny minority seems to have influence all out of proportion to its size. It is nonpareil at establishing factoids (or "memes" in the current jargon) that survive and propagate, almost independent of any evidence to support their validity.

So like it or not, and I often don't, I track half a dozen newsgroups. I choose them primarily because of their importance to my company, Dinkumware, Ltd. That means a heavy focus on things C, C++, Microsoft, and ISO standardization. I speak up when I think I really have to, but mostly I lurk and take the temperature of each forum on various topics.

You can tell a lot from the questions that keep popping up. The really obvious ones end up in a FAQ (Frequently Asked Questions) file for each newsgroup. It behooves a newbie to check out the FAQ file before using up air time asking the obvious. (Not everybody is this courteous, of course.) But those aren't exactly the questions I'm talking about. Rather, it is the FAQs in waiting that I focus on, the questions frequently asked but not always answered the same way by the self-appointed experts on the newsgroup. I think of these as Frequently Answered Questions.

I've chosen a handful of what I consider the more important recurring questions to answer here. Of course the answers I supply are just my opinion, however well informed. You can decide for yourself how large a grain of salt to add to each one.

Q: Are there any compilers that fully conform to the C++ Standard?

A: Probably not, because the C++ Standard is huge and has several spots that are vague or recognized as just plain wrong. It will take a couple of years to sort out more exactly what we mean by conformance.

But a couple existing compilers come pretty close. To the best of my knowledge, the latest Visual Age compiler from IBM is about as close as a typical implementation is likely to get. You have to be a language lawyer, looking for trouble, to notice the few deviations. The compiler itself was developed in-house at IBM. It represents a radical new departure in incremental compiling, keeping track of code changes down to the expression level. The library comes from my company, Dinkumware, Ltd. (Why this offering from IBM is not more popular is beyond me.)

Metrowerks claims a fairly complete implementation as well, though I haven't kicked the tires lately. Once the compiler vendor of choice for the Macintosh platform, Metrowerks is aggressively repositioning itself as a cross-platform, multilanguage vendor as the Mac market dries up. They have their own compiler developed in-house. They also adopted the Standard C++ library from Modena and rewrote it extensively in-house to improve its completeness, correctness, and conformance.

Also very close are the compilers licensed by GHS (Green Hills Software). They benefit from the excellent, and widely used, compiler front end from EDG (Edison Design Group). Until very recently, the EDG front end did not support a couple of esoterica such as template templates, but it is otherwise very complete and has been for a couple of years. GHS also ships the Dinkumware, Ltd. Standard C++ library, so their compilers include a complete library, just like IBM. GHS offers a host of cross compilers aimed primarily at embedded software development.

Kuck and Associates combines the EDG front end with their own reworking of the Modena library. Their target marketplace seems to be desktop workstations running Unix, where support for highly optimized numeric computation can command a premium price.

The other big PC-based compiler vendors, such as Microsoft and Borland, still have several language features to add. Microsoft ships the Dinkumware library, but it is restricted in a few small ways because of missing language features. Borland ships the Rogue Wave library, which is rather less complete and similarly restricted.

Finally, the gcc compiler from Project Gnu is being steadily upgraded to incorporate the latest language features from the C++ Standard. Until recently, new development took place under the "egcs" rubric, but egcs has rejoined the mainstream as of gcc version 2.9. Standard C++ libraries are being developed by Dietmar Kühl, Cygnus Solutions, and SGI (Silicon Graphics Incorporated). SGI offers the most popular implementation of the Standard Template Library, at least as free software goes — they are steadily fleshing it out to make a complete Standard C++ library (which is rather more than just STL and traditional iostreams). All three of these library efforts have a ways to go before they're competitive with commercial offerings, however.

Q: Why don't more compilers conform to the C++ Standard today?

A: The C++ Standard froze technically in November 1997. Two years seems like plenty of time for all the players to fall into line. After all, when the C Standard froze there were dozens of compilers claiming conformance almost immediately. You'd think that C++ would shape up even faster in "Internet time."

Comparisons are often less obvious than they appear, however. The biggest difference is that Standard C++ is a much more ambitious language than Standard C. It is not unusual for C++ compilers and libraries to be ten times bigger than for C, just counting lines of source code. (And complexity increases much faster than in direct proportion to code size.) Writing a C++ compiler is such a big job that you can count on your fingers all the reasonably complete compilers. Implementations of the Standard C++ library are even fewer in number. (I think I mentioned every single one above.)

So why haven't this small handful of players all matched the final C++ Standard? Aside from the sheer complexity of the spec is its newness. Standard C++ evolved rapidly and steadily over an eight-year period. Vendors who chose to implement features along the way often found themselves with work to undo as the spec evolved. Those with a large customer base face the quandary of backward compatibility. Not only do they have to keep support for "traditional" (pre standards) C++, they now have to maintain various language and library features that have changed along the way.

Those with the patience to keep reworking their implementations were faced with significant changes right up to the very end. (A nontrivial library change was approved literally minutes before the final vote to freeze the technical content of the C++ Standard.) By contrast, the Standard C language hardly changed at all in its last four years of standardization. Additions to the library in that time frame always permitted a trivial implementation that was arguably conforming, for those who didn't really care about the very newest features. It was much easier to catch up and keep up with Standard C.

Finally, there is the issue of competing needs. Standards conformance is just one need. And it is important only to the extent that customers understand what it means and ask for it. Compiler vendors must also keep customers happy in other arenas — improved performance, better integration with other languages, improved libraries for windowing or multithreading, better integrated development environments, etc., etc. The list is endless. The features that marketing wants now always far outstrip development resources. Bottom line: standards conformance becomes important to implementers only when it becomes sufficiently important to programmers that the message is heard above the din.

Some vendors, Microsoft in particular, are often accused of intentionally subverting standards to lock in customers. I've seen no evidence of this in the world of compilers, at least not with any of our customers. But it is fair to say that standards conformance is not always at the top of the list for compiler vendors. And it won't be unless and until their customers in turn put it there.

Q: What happened to all the traditional C++ headers with names like <iostream.h>?

A: None of the C++ headers with names that end in .h are part of the Standard C++ library. They are replaced with dozens of new headers with no suffix at all, such as <iostream>. Many vendors continue to ship a traditional library, which you invoke by including headers such as <iostream.h>. A few supply headers with the old names as a bridge — they turn around and include the new headers as appropriate, so you don't have to change your existing code quite so much.

But be warned about mixing old headers and new. You might well end up mixing libraries that are not intended to work together. A recurring plea for help on the newsgroups involves a code snippet that looks something like:

#include <iostream.h>
#include <string>
     
int main()
    {     // say hello from a string
    string str("Hello, world");
    cout << str << endl;
    return 0; }

If the first header includes a traditional iostreams library, it knows nothing about new-fangled strings. Hence, you get mysterious diagnostics to the effect that class string has no suitable inserter. (It does, but not for the older flavor of class ostream.)

It's nice if an implementation lets you compile existing code unchanged, but once you start adding new code, be careful. As soon as you find the need to mix old and new library headers, take a deep breath. It's time to phase out the old headers before you proceed.

Q: Okay, I've updated my traditional "Hello, world" program:

#include <iostream>
int main()
    {       // say hello
    cout << "hello, world" << endl;
    return 0; }

Why does it still not compile?

A: Standard C++ does more than just change library header names. It also makes use of a new language feature called namespaces. Essentially all library names (macros are an obvious exception) are defined in namespace std, ostensibly to minimize collisions with names defined by the programmer. You can fix the program above by adding qualifiers, as in:

#include <iostream>
int main()
    {       // say hello
    std::cout << "hello, world"
              << std::endl;
    return 0; }

Alternatively, you can add one blanket declaration that hoists all the names out of namespace std back into the global namespace, as in:

#include <iostream>
using namespace std;  // hoist names
int main()
    {       // say hello
    cout << "hello, world" << endl;
    return 0; }

While frowned on by purists, the latter form localizes the necessary changes to the top of each program file, where you already have to change include directives. I consider it a sensible way to migrate old code. Support for namespaces is still spotty across current implementations. If you need to write portable code, you can more easily do so with a bit of #ifdef logic in the same spot.

The experts are still debating how best to use namespaces for their intended purpose. Unfortunately, the Standard C++ library is intimately associated with namespace std. You can't avoid mentioning it at least once in every program you write that uses the library. Failure to do so is easily the commonest source of confusion among programmers migrating to Standard C++.

Q: Okay, I understand that all library names are now defined in namespace std, but my compiler apparently hasn't changed the Standard C headers as required. Why not?

A: The C++ language has for many years imposed additional requirements on the headers from the Standard C library. Just list one from your favorite compiler and look for all the code bracketed by:

#ifdef __cplusplus

...

#endif

The macro __cplusplus is defined by C++ compilers, but not by C compilers, so the bracketed code is an addition of interest only to the former. It has no effect on C programs. The C++ Standard adds a host of additional requirements. Most of these simply result in still more conditional code of the same ilk. But one added requirement stands out from the rest.

According to the C++ Standard, even traditional Standard C library names are defined in namespace std. If you include a header such as <cstdio>, that's the end of it. You have to refer to, say, std::FILE instead of FILE to make use of the names. The traditional header <stdio.h> is effectively defined in terms of <cstdio>. It behaves something like:

#include <cstdio>
using std::FILE; // hoist each name
...

Hoisting the names one at a time has a slightly different effect than the blanket using directive I've shown above, but I won't go into that here. The important message is that the C++ Standard no longer views the traditional Standard C headers as a foundation upon which to construct the Standard C++ library. Rather, the latter is fundamental and the former is derived. In the real world, of course, this is far from the truth. Every C++ implementation sits atop a C implementation, which has been shaped by decades of code development. It's one thing to require still more snippets of code to be added to the Standard C headers — dislodging them from their central role is quite another thing.

So for a host of reasons, none of the major compiler vendors have so far chosen to do exactly what the C++ Standard says with the Standard C headers. It is possible to doctor up the headers to conform — I did so with my Standard C library years ago — but the ramifications are both subtle and pervasive. The administrative problems alone have discouraged some companies from making the switch.

I should emphasize that the C++ standards committee was petitioned several times to back down on its required changes to the Standard C headers. The last request came just months before the C++ Standard froze. But the committee was adamant, so the requirements remain.

Some vendors today just leave the Standard C headers alone. They define all names in the global namespace and leave them there. A header such as <cstdio> simply includes its alter ego, <stdio.h>. (This is the current Microsoft approach.) Others leave the Standard C headers alone, but attempt to approximate what the C++ Standard requires of the new headers. (This is the scheme used by IBM, among others.) With this approach, the header <cstdio> looks something like:

#include <stdio.h>
namespace std
    {       // pull in global names
    using ::FILE;
    ... }

This approach fails to conform in ways both overt and subtle. Including <cstdio> is not supposed to define names in the global namespace, but clearly this version does. More subtly, function overload resolution does not behave exactly as it should, because the library names start out in the wrong namespace. (Once again, I gloss over the details.) But for most needs, it comes close enough.

My recommendation is to adopt a style that is maximally portable across diverse implementations. Include the header <stdio.h> if you assuredly want names defined in the global namespace. Include the header <cstdio> if you assuredly want names defined in namespace std. Always assume that the names might be defined in the other namespace as well. It ain't Standard C++, but it's real life for the foreseeable future.

Q: I've always been careful to check my allocations, as in:

Thing *p = new Thing;
if (p == 0)
    <recover>

But now my program aborts instead of executing the <recover> code. What am I doing wrong?

A: Nothing. It's just that the rules have changed under foot. In the early days of C++, a new expression indicated failure by returning a null pointer, much as malloc does in the Standard C library. But Standard C++ calls for different behavior. Now, a new expression indicates failure by throwing an exception of class bad_alloc. An uncaught exception aborts execution.

If you want to reclaim your recovery code, you have two basic choices. One is to move into the world of exception handling, as in:

#include <new>
...
try
    {       // attempt allocation
    Thing *p = new Thing; }
catch (bad_alloc)
    {       // allocation failed
    <recover> }

The other is to use a new version of new (as it were):

#include <new>
. ....
Thing *p = new(nothrow) Thing;
if (p == 0)
    <recover>

Technically, the argument to new makes it a "placement new" expression, which lets you smuggle in additional information. In this case, nothrow is a constant of class nothrow_t. It's sole purpose is to convince the expression to return a null pointer instead of throwing an exception. Just like in the old days.

It's regrettable that backward compatibility suffers here, as it does with the use of namespaces. But in both cases, the committee favored coding practices they consider safer over the need to preserve working code unchanged. Perhaps future programs will be safer, on average. Meanwhile, lots of C++ programmers are frequently asking questions.

P.J. Plauger is Senior Editor of C/C++ Users Journal and President of Dinkumware, Ltd. He is the author of the Standard C++ Library shipped with Microsoft's Visual C++, v5.0. For eight years, he served as convener of the ISO C standards committee, WG14. He remains active on the C++ committee, J16. His latest books are The Draft Standard C++ Library, Programming on Purpose (three volumes), and Standard C (with Jim Brodie), all published by Prentice-Hall. You can reach him at pjp@plauger.com.