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

Ianjoyner
9 min readJul 1, 2024

--

This is the first of a series of articles examining Bjarne Stroustrup’s writings about the core foundations in C++. I will link to the subsequent articles from here as I post them.

This article examines the early core philosophies of C++. We might think that early philosophies, whether they are wrong or became wrong due to changing technology, might be updated to reflect modern problems (security for one) and technology in faster hardware and better language and compiler technology.

Much of the thinking behind C and C++ were compromises based on the constraints of technology of the time, as well as just being wrong. But these limitations were baked into the core of C++ as foundational philosophies, rather than things that could change. The fact is that early core mistakes make a design more difficult to get right in the future. C++ has now gone through decades of trying to get it right. We can retrospectively look back and trace why C++ has never got it right.

Part 2 System Programming and Application Programming, An Old, Command-and-Control Language

Part 3 Abstraction, Fixing C

Part 4 Purposes of a Programming Language

Part 5 Checking and Helping Programmers to get things Right

Part 6 Origins of Class

Part 7 A Question of Style, Rules, and Freedom

Part 8 Whom do we kill? No Conclusion

Core Philosophies

The core thinking in a language is difficult to change — it forms everything else upon which the language is based. Cursory issues such as deleting old features and cleaning up syntax, should present opportunities. But as we shall see, in C++ even these simpler issues are difficult to address due to both the obsession with backwards compatibility and the resistance from the user community.

We start in 1986 with quotes from the book ‘The C++ Programming Language’. The same core philosophies are repeated 10 years later in the third edition (1997). Skip another 10 years to an interview with Stroustrup in ‘Masterminds of Programming’ and the same thinking is evident. I reserve comments on the answers there to another article (this one is already overly long, but necessarily so).

The first edition of the 1986 book was made to mirror Kernighan and Ritchie ‘The C Programming Language’ in title, form, and size. I have gone back to this early 1986 book because it reflects the basis of the language and some of the short-term thinking. Much of this was reproduced in the 1997 third edition. The third edition was nearly three times the length at 910 pages up from 328. This edition is not just a few new features such as multiple inheritance and templates (lacking in earlier versions) but explaining the original compromises. The fourth edition was not published until 2013, which included C++11.

In retrospect, I see two problems. The first is ideals that just were never lived up to. The second is the wrong ideals that were lived up to. The real ideals of programming are difficult to realise. The wrong ideals are mainly compromises that are contrary to the real ideals. Such compromises are taken because they are easier to implement than getting it right. This can be because of limited technology of the time. Some things just didn’t deliver, like this first point on page 3 under ‘Design Notes’.

Simplicity

Simplicity was an important design decision”. (1986 page 3).

Well, there’s a complete failure. What happened? Simplicity is indeed one of those real software ideals. It is a fundamental goal of computing to tame complexity with simplicity, not pile extra complexity on top. Taming complexity is about discerning the real essence and ignoring the irrelevant. That is abstraction.

It sounds ‘simple’, but simplicity is difficult to achieve. Programming has essential complexities that are intrinsic to the problem. We can handle those complexities directly, but C++ introduces accidental complexities in the solution.

C++ has become one of the most over complex behemoths in the industry, and it was pretty much that from the start, even before multiple inheritance, templates, namespaces, etc, etc. Thus simplicity is an ideal that C++ failed to meet. Instead of being honest about the problems in C, C++ rather glossed over them, trying to fix by stealth and not upset the C brigade, who still complained anyway.

…where there was a choice between simplifying the manual and other documentation or simplifying the compiler, the former was chosen.” (1986 page 3). This also seems not to have worked. In fact, the reverse seems to have happened, things that a compiler should handle are left as burden to the programmer and this significantly impacts the documentation with more complexity. The C philosophy was always to not handle things in the compiler if they could be pushed to the programmer, like pointer referencing and dereferencing, and explicit pass-by-reference. This was at odds with this stated C++ aim, so it is no wonder we ended up with an odd mix of conflicting philosophies.

Terry Lambert notes about new features in C (not C++): “Most of these features have been to make programmer’s lives harder, and compiler writer’s lives easier.

https://www.quora.com/Will-C-get-new-features-in-the-future/answer/Terry-Lambert

It is far worse with C++, but C++ compilers are notoriously difficult to produce.

The Curse of Backwards Compatibility

Great importance was also attached to retaining compatibility with C, this precluded cleaning up C syntax.” (1986 page 3). More on compatibility is in (2013 page 14).

Thus C++ was very much constrained in what it could do and help move the activity of programming forward, rather keeping it shackled to the past and what was wrong in C. C++ wanted to capitalise on the popularity of C (even though much of that popularity was misplaced). Long-term gains were lost for short-term foci. Programmers should have been helped or gently coaxed to move forward. But C people being C people were resistant. The hype had established that C was a perfect language and that nothing else was needed. C++ also acquired this misplaced mystique.

C++ types and data-hiding features rely on compile-time analysis of programs to prevent accidental corruption of data. They do not provide secrecy or protection against someone deliberately breaking the rules. They can, however, be used freely without incurring run-time or space overheads.” (1986 page 3) (2013 page 13).

Checks that prevent unintended mistakes are all very well and good. However, it is the deliberate hacking that C++ allows for the false justification of not incurring run-time checks that is a real problem. It is paradise for malicious hackers. Pointers easily undermine data hiding and encapsulation. This is the weakness of C and C++ and is indeed the $trillion mistake.

We might save the processor a few cycles, but the cost of passed to the entire world. The whole philosophy comes from a different time when computers were in house and shared by friendly cooperative people, particularly in universities and research laboratories like Bell. Bell was not a vendor to commercial customers like banks where security was very important, even in early days.

C++ was primarily designed so that the author and his friends would not have to program in assembler, C, or various modern high-level languages.” (1986 page 4) (2013 page 23).

This seems to justify that everyone should design their own language so that they don’t need to write in any other language. C++ suffers from the primitiveness of assembler and C, but has little of the benefits of true high-level programming. It is a limited vision — C was not so much a language design, but an expedience to not have to write Unix in assembler, again that is a limited vision. Viewing high-level language as just a replacement for assembler is a very limited view of what high-level programming is about. It fact, it completely ignores the point. There is so much more to high-level, structured, and OO programming than just syntax.

The 2013 edition expands on the above “C++ was designed primarily so that my friends and I would not have to program in assembler, C, or various then-fashionable high-level languages. Its main purpose was to make writing good programs easier and more pleasant for the individual programmer. In the early years, there was no C++ paper design; design, documentation, and implementation went on simultaneously. There was no ‘‘C++ project’’ either, or a ‘‘C++ design committee.’’ Throughout, C++ evolved to cope with problems encountered by users and as a result of discussions among my friends, my colleagues, and me.”

This really shows the haphazard approach to the design of C++.

Avoiding writing in assembler was an aim of C, from Burroughs ALGOL where there was no need for any assembler — the machines didn’t even have an assembler. However, Burroughs focus was not just to replace assembler but as an overall system design to support high-level programming in its true intent. Robert Barton makes this point in the following paper:

https://www.scribd.com/doc/61812037/Barton-B5000

This was a new approach in 1961 — it is still a new radical approach today.

But C was seen as a coding language over assembler, rather than a truly structured-programming language. C is not a high-level language. Pointers (from B/CPL) are machine oriented. Macros (also adopted from Burroughs ALGOL from a suggestion of Donald Knuth) are primitive text shunting. C compromises high-level programming, even at the system level. C++ does worse for OO, getting some structuring benefits from the syntax of classes and inheritance (and later other things such as multiple inheritance and generics as primitive templates were added), but not getting the full benefits of the approach.

The work on ‘C with Classes’ (the base of C++) began as an attempt to modularise the Unix Kernel. From ‘Design and Evolution of C++’: “The work on what eventually became C++ started with an attempt to analyze the UNIX kernel to determine how it could be distributed over a network of computers connected by a local area network. … Two sub-problems soon emerged: how to analyze the network traffic that would result from the kernel distribution and how to modularize the kernel.”

Presumably the network traffic is overhead of calls between kernel modules that would have previously been a simple call. RPC is a silly idea — the latency of network transmission time mostly exceeds the processing time (read Roy Fielding’s thesis on REST). There would be little benefit in a distributed kernel, but there is benefit in distributed systems at a high level. It seems C++ has never been used to any great extent in Unix itself. Ken Thompson is scathing of C++. The Unix people went on to design Plan 9 for distributed systems. I don’t think C++ would have been used (but even they could not overcome the popularity of Unix to establish something more advanced). Then Linux completely rejected C++ — Linus Torvalds’ remarks on C++ are famous.

http://harmful.cat-v.org/software/c++/linus

In particular, he condemns using C++ in the kernel, which from the above was one of its original intentions:

Trust me — writing kernel code in C++ is a BLOODY STUPID IDEA.” — Linus Torvalds

Some C++ is reputedly used in Windows, but this is proprietary closed source. Thus C++ has not been much of a success in system programming. C++ is a system language. Perhaps C should be used for low-level kernel, and C++ for higher system levels? This is indeed Burroughs (Unisys) approach with the MCP OS written in the NEWP adaptation of ALGOL, and environmental middleware software being written in DCALGOL (for Data Communications, although that is a misnomer) which includes queues at language level for server programming. Then there is extended ALGOL itself for utilities and customer system programming. This ensures separation of concerns between system levels. This is the correct way to do layered system software.

But C and C++ have no such clear division and since C++ has not been so widely adopted for system programming they have instead inflicted it on application programming. C and C++ have become competitors, not cooperators at different system levels. Most projects use one or the other, except C++ is mainly used as a better C, not as an OO language (since the point of OO is missed). I contend C++ is even less applicable at higher levels. Remember those “various then-fashionable high-level languages” that Stroustrup and his friends did not want to program in — that is exactly what they are for.

There is much more to come. I will link here as the further articles are posted.

Part 2

--

--