The Casting Vote

by Sean A. Corfield

Valley Forge, Pennsylvania. The scene of some famous battles. November 1994 saw the latest meeting of X3J16/WG21 at the Convention Center nestled between some freeways in a particularly pedestrian-unfriendly manner. Perhaps in keeping with the spirit of the location, some skirmishes were fought between factions of the joint C++ committee. Unfortunately, they were over 'minor' issues of syntax with some added political spice thrown in.

History in the making

However, history was made in a way, when the joint committee voted to remove parts of the draft standard library - whoever heard of such a thing? We all know that standards committees only add stuff, don't they? Under the hammer this time were early attempts at container classes and some of the more baroque parts of the streams library.

With the introduction of STL in July 1994, we now have a very consistent framework under-pinning the library and efforts are being made to coerce other parts of the library into the 'style' of STL - templatising strings and streams, adding iterators and generally making the class interfaces more consistent. STL provides a vector container which makes the classes bit_string, dyn_array and ptr_dyn_array redundant since the former can be replaced by vector<bool> and the latter by vector<T> and vector<T*> respectively with no loss of functionality.

The streams library also underwent some minor surgery with the loss of stdiostream in favour of fstream. The intent of stdiostream had been to aid mixed stdio / streams programs but the semantics of fstream have been gradually changed so that such help is no longer necessary. Another victim of the streams library's growing pains is strstream - the sscanf / sprintf of the streams world - which has been deprecated (i.e., marked for potential removal in a future version of the standard) because stringstream provides a safer alternative using the library class string.

Exceptional politics

The UK have raised the objection that exceptions break pretty much every program written. The following code fragment is a good example of how exceptions break things:

void f()
{
	T*	p = new T;
	// some processing
	delete p;
}

If the processing causes an exception to be thrown, the statement delete p; is never reached leading to a memory leak. To make this code "exception-safe" requires adopting the "initialisation is acquisition" idiom associated with resource management:

class T_handle
{
public:
	T_handle(T* pp)
	: p(pp) { }
	~T_handle()
	{ delete p; }
	operator T*()
	{ return p; }
private:
	T*	p;
};
void f()
{
	T_handle p(new T);
	// some processing
	// p automatically
	// deleted when it
	// goes out of
	// scope or when
	// exception thrown
}

The UK viewed this as such a fundamental idiom that we required a solution before we would vote "yes" in the Committee Draft Registration ballot (currently in progress - see "The Casting Vote" in CVu7.1 for more details of the ballot process). Greg Colvin has produced several proposals covering "smart pointers" and garbage collection and the committee finally adopted one of his proposals in an effort to satisfy the UK and other ISO members. The class template is called auto_ptr and is used in a similar manner to the example above. He also proposed counted_ptr which can be used to underpin a reference-counted object, but this was presented in a somewhat unconvincing light by the chair of the Library WG who disapproves of what he considers "ISO blackmail" when a "yes" vote depends on acceptance of some language feature or library class. This view is shared by several members of the ANSI committee, as I have commented before, and does not make international consensus-building any easier. The effect was that the ANSI vote went against counted_ptr but the ISO vote was in favour. After some political manoeuvring which saw various countries changing their votes in an attempt to improve consensus, the motion was withdrawn with an agreement to bring it back for a vote in Austin, TX in March 1995. The battle is still being fought on the committee reflectors but at least some of the political aftertaste has worn off.

Exceptional contracts

What do exception specifications do for you? The answer is probably "not much". You can specify in a function declaration what exceptions a function can throw but how much confidence can you have in such a declaration? Until Valley Forge, the answer was "very little". The reason is that when the compiler sees an exception specification, it has to generate code to trap any other exceptions and call unexpected(). That function in turn can throw any exception it fancies, thus breaking the contract suggested by the function's exception specification. This was another UK bug-bear and possible solutions involved removing exception specifications from the language or having them compile-time checkable, neither of which won universal favour with the committee. In the end, the Extensions WG provided an acceptable half-way house: unexpected() may not throw anything that would break the contract. This means that a compiler can perform reasonable checking on exception specifications and provide optimisations where it can establish that unexpected() cannot be called. This in turn makes using exception specifications much more attractive and seems to satisfy several different groups all at once.

Templates - and where to stick 'em

Probably the most difficult aspect of portable C++ programming is how to organise your template code so that it compiles on a variety of platforms. (Unless, of course, you happen to be a Microsoft C++ programmer in which case you don't have to worry about such things unless you have NT and can run VC++2.0) For Borland programmers this is predetermined - you simply include all your definitions and the compiler figures it all out for you. For Cfront programmers it is also well-defined - if your declarations are in x.h, your definitions live in x.c. This is fine until you try to port code between those two environments so for multi-platform tool vendors it is a real and very costly issue.

Template compilation was another issue that the UK felt very strongly about and the Extensions WG have worked very hard to come up with a possible solution. To be more precise, Bjarne Stroustrup has worked very hard on this. The previous best effort was a new directive that told the compiler where to look for template definitions. This was a compromise between a preprocessor directive favoured by some compiler vendors and a completely automatic, or magical, system preferred by others (and most users). In the end, the scheme adopted means that templates behave pretty much like everything else in the language. Declarations live in header files and definitions live in source files. You can put definitions in header files if you want - they have to be static or inline - because they behave just like non-templates. While this makes life much easier for users, it does make life harder for vendors. Hopefully we will all benefit from this (major) change.

Being explicit

As you might expect, we voted in another keyword. Before you start howling, let me step back and give some background. How often have you been caught out by a constructor with one argument? Implicit conversions are wonderful when you want them but they have a tendency to pop up in unexpected places. Consider the following code fragment:

void f(string);
f(5);

Surprisingly, this is a valid call because an implicit conversion exists from 5 to a string. But what does it do? Does it create a 5-element string or a single element string with a character whose value is 5? A good knowledge of the class is needed to decide, and yet this is likely to be quite common. How can the class designer prevent these implicit conversions? Current tricks involve adding a dummy argument which the user must specify or using an intermediate conversion class. Both these solutions are "clever", "ugly" and have slightly undesirable semantics. Nathan Myers proposed adding the keyword constructor to the language to specify a non-converting constructor (the keyword destructor was proposed for symmetry). The proposal was generally well-received. In fact, Germany had made the issue of non-converting constructors one of their "no" vote issues. Unfortunately, when the proposal came up for discussion in full committee, several people objected to the syntax. Round and round we went, with all sorts of interesting suggestions until the committee accepted a single new keyword explicit. You can declare a constructor explicit and it will no longer be available for implicit conversions.

Wot! No Core?

Of course the Core WG was busy! This time they worked on object shape acquisition and the const-ness of objects. They also revisited polymorphic behaviour during construction. If you find this a totally fascinating subject, please e-mail me to discuss it but I'm not going to delve into such a snake-pit in public!

Coming soon...

The next Casting Vote column will tell you what happened in Austin, TX in March 1995. We will know the result of the CDR ballot then and I should be able to report on whether we started the CD ballot.

Sean A. Corfield