The Failure of C++

The strong, static system of C++ seems to provide almost everything that the weak and dynamic type systems of Perl and Python do, but also adds an extra, automatic error checking step. It’s a bit perplexing as to why C++ seems to fail to provide overall advantages in development efficiency or extensibility. The failure can be attributed to extra development cost to maintain parallel type calculations throughout the software and also to the failure of type checking to provide a sufficiently broad proof of the program’s self-consistency.

In order to compile, the types must be consistent. Unfortunately, as C++ currently stands, the majority of this consistency checking falls in the hobgoblin category. The software developer must redundantly calculate the result type of almost every expression. This is a useless exercise that adds no information to the program (the compiler independently calculates the types anyway). In some cases the return type of a function is considered an implementation detail that should not be exposed to the application design (for example, boost::bind). Here it is impossible (or at least detrimental) to reflect the return type. This makes a noticeable reduction in the expressive power of such libraries. C++ 0x may help with auto and valtype keywords. Since the “initializer expression” may not be readily available for class member variables, these keywords will add some asymmetry between state variables and scoped variables. One should probably think of scoped variables as descriptive names for subexpressions of a function rather than simple values.

The compiler’s verification that the types in the software are consistent should give an indication that the software is to some degree correct. Evidently, that is not the case as C++ software needs essentially the same testing regimen that weaker languages do. Two aspects of the language contribute to the disappointment in C++ software robustness. First, the standard library offers no assistance. For example, there is no “type qualifier” mechanism for doing things like dimensional analysis or taint analysis. Without library support, it is difficult to make use of the type system for verifying any non-trivial degree of consistency within the program. The second factor is the asymmetry between types and values. Although most test cases could be evaluated statically, there is no way to induce the compiler to make the evaluation and assert an error on failed test cases. Without this integration, software development organizations must invest in a substantial infrastructure to organize, compile, and execute specialized testing projects.

At a higher level, the case of program validation with C++ could be vastly improved if it had some facility for introspection and pattern replacement on its abstract syntax tree. It’s hard to understand how any modern language fails to provide this bit of introspection. One would like to inject new rules into old software: for example, to deal with instances of ‘new’ where the result is not immediately assigned to a smart pointer (replace the raw pointers to shared pointers or issue errors). Presumably you will learn more interesting and powerful practices in the future that would be beneficial to apply to the code that is being written today.

Integration of the abstract parse tree with the code editor opens up some interesting possibilities. For example, pattern replacement operations in the previous paragraph could be represented within the editor. At SciFoo Camp, Charles Simonyi and Ted Kaehler both described work on development environments that would have the ability to render different views of some underlying syntax tree. UML has similar ideas, but in UML the focus is on integrating different levels of abstract design rather than elucidating the implicit relationships within an actual piece of software.

Leave a Reply