The Foundations at the Core of C++ are Wrong — Part 3

Ianjoyner
7 min readJul 2, 2024

--

This is the third of a series of articles examining Bjarne Stroustrup’s writings about the core foundations in C++. The first part is here with links to the other parts:

Part 2

Abstraction

Abstraction makes things clear and gives meaning. Abstract art has often been misunderstood as the reverse since people often say they can’t work out what it means, but abstraction is the process of extracting and preserving meaning by focus on the essential, ignoring the irrelevant. Because of the ‘abstract art’ effect, people fear abstraction. Abstraction is based on distilling the essence of a concept. Abstract art can remove the familiar which people associate with the concept, but perhaps falsely so. In computing when people are taught from the low level up and are then told they should not use primitive features like gotos or pointers or other things they object to having the familiar removed. This seems to have been a problem that C++ had trying to remove the dubious features of C.

In programming, essence depends on the purpose of a system and the roles of entities within a system. Essence is both independent of context and dependent on context. Essence is independent of context in the sense that if a module (in the general sense) is moved to another system (change of context) it remains the same and no change is needed. This gives us useful reusable software. However, we also develop modules depending on the context of the system we are developing. The context that we are dependent on is the conceptual context — we should be independent of the implementation context.

This is the problem with C and C++ — they are far too dependent on implementation context that good programs should be separate from. Concept is not clearly separated from implementation.

In C and C++ far too much of the programmer’s attention is directed towards the languages themselves rather than the entities we are trying to specify. This is like machine-level assembler coding where focus is on the machine and how it works, rather than the problem. A language should be as unobtrusive as possible. The language should support the programmer, not the other way around, implying bending programs to suit either the language or the platform. Abstraction helps that level of independence.

Without abstraction we could hardly develop large, complex systems. Abstraction is separation of concerns meaning we can divide and conquer complex software into layers. When we write software it is generally on top of a lot of other software that has abstracted lower-layers for us, all the way down to working out where and how to store things on persistent media (disk or SSD).

A common misconception is this kind of layered abstraction results in poor performance, calling down through layers. This is wrong for several reasons. Often a compiler will generate machine code directly, removing the abstraction completely. The second reason is that the layers provide functionality we would otherwise need to provide in our program, which adds the layer anyway.

Often a layer will be written by experts at that layer who will know and profile how to do that in the most efficient way, better than most other programmers will. Thus the layers result in us not having to provide the functionality ourselves. There is no saving in any time. The layers provide functionality that will need to be provided somewhere.

C does not actually do what a hardware-controlling language should do (from what Perlis said). It is not ideal for higher-system levels either (again from what Perlis said), and certainly should not be used for application programming, even though it can be. C++ at least highlights that C has many problems, probably the worst of which are lack of modularity (modules are primitive based around header files), and lacking cohesive type system. The problem is C++ hardly solves these deficiencies, both in design and the way people just use C++ as a slightly better C.

When Should Abstraction be Taught?

On Stroustrup’s pages there is a review by Marshall Cline, writer of the C++ FAQ of the second edition of ‘The C++ Programming Language’. Cline’s comments address when abstract ideas should be taught in programming.

“The general approach (language first, then design) is one that I personally find easy to teach from.”

I would say both need to be taught in tandem. It also depends on the student. It is actually dangerous to make a student only understand principles in one language — they naturally attach the principles to the language and then have difficulty separating the two to apply the principles in different contexts. This locks them into a language. This has been a persistent problem in computing that people can’t separate the concepts from particular expressions of those concepts.

“Some will argue with me on this point, but both my learning and teaching experience has taught me that people learn first concrete, *then* abstract. “One apple and two apples make three apples’’ is mastered long before “x + 2x = 3x”.”

That is not true. Children are taught arithmetic operations in abstract form from a very early age without needing to think of apples. They easily think “1 + 2 = 3”. (Note how Cline and I are using ‘=‘ correctly!). They can apply it to apples, oranges, or whatever fruit takes their fancy. They can even think in terms of adding one apple to two oranges. Then what do we have? We have three pieces of fruit. Children can understand this generalisation to fruit. Abstraction is not actually difficult.

“…These decisions fit the “Just In Time” approach to education: don’t teach an abstract (and hence powerful) concept until the student is ready to tackle problems that *require* the extra power. The abstract concepts are provided “just in time.” The student is thereby provided with a built-in motivation, and never has to ask, “Why am I bothering to learn this?””

Again I think this is simplistic. I suspect it is a justification on both the part of Stroustrup and Cline to only teach C++ is a way that students are locked into C++, not able to separate the essential concepts from arcane structures in C++. Certainly I agree that teachers must motivate students as to why what they are learning is important. Teachers are often afflicted with the ‘curse of knowledge’, that is assuming that others understand what they are talking about, but often they don’t have the background. Students should always be questioning “Why am I bothering to learn this?” That is a good question to ask.

Abstract (and thus computational) thinking should be taught from the earliest stages.

Fixing C — well, no!

Clearly some problems could be avoided if some of the C heritage was rejected. This was not done because (1) there are millions of lines of C code that might benefit from C++, provided that a complete rewrite from C to C++ were unnecessary; (2) there are hundreds of thousands of line of library and utility software written in C.” (1986 page 5)

First there is the admission that there are problems, actually many problems in the C language. But the justification of not doing more to remove these problems means (as Alan Kay says) the old is just perpetuated, and programmers fail to understand what is good about the new, they just keep using the old. This means that programmers remain locked into the past, rather than helped to move forward. The only moving forward in C++ results in even more lock in. The millions of lines in C is testament to the lock-in factor of C, that is lock in to details that programs should be released from by a language. K&R say that C is a low-level language in both 1st and 2nd Edition:

“C is a relatively “low level” language. This characterization is not pejorative; it simply means that C deals with the same sort of objects that most computers do, namely characters, numbers, and addresses.”

Not just low level, but a primitive coding language dressed up with some structured syntax, while still retaining non-structured facilities. Computers deal with characters (mainly in textual strings) and numbers because they are common to most applications, so processors include special hardware for performance. However, addresses are internal to a machine, and programmers should not be required to think of addresses, except perhaps for the lowest levels (although even there it is debatable).

Many people mistakenly think or claim that C is a high-level language. This is confusing syntax that resembles structured syntax with high-level programming. C is far more oriented to machine-level programming than it is to problem-oriented programming.

Stroustrup: “The base language, the C subset of C++ is designed so that there is a very close correspondence between its types, operators, and statements and the objects computers deal with directly: numbers, characters, and addresses.”

This thinking is the wrong way around. Computers were built with units to handle numbers and characters (strings) because that is what is needed in applications. Even these are not intrinsic to computers — they are included because that is what applications and programming need. C and C++ look at this upside down and cater to the machine rather than the problem. C and C++ make programming subservient to the machine — it should be the other way around. Refer to Barton’s paper above.

Part 4

--

--