About forty Christmases ago I was absolutely delighted to open a slot car racing set from my parents. I feverishly set everything up, created a track reminiscent of Brands Hatch, and went to plug everything in, only to discover that I needed batteries to operate the controllers. This was Great Britain in the 1970s. There were no shops open on Christmas Day. There were no appropriate batteries in the house, nor in our neighbour’s house. My delight was transformed to desolation. My parents had an argument. My grandfather distracted me with a story about one of his second world war sorties over the French coast. I watched a James Bond film sulkily. All because there were no batteries in the box.
What prompted this blog post?
With the next C++ Standard meeting in Jacksonville bearing down on us, I have just submitted the next version of the 2D graphics paper, https://wg21.link/P0267. One argument levelled against it runs along the following lines:
“A graphics library shouldn’t be part of the standard library. The standard library is too big already, and this will make it so much bigger. It’s just not appropriate. The library should only contain small types.”
Of course, I disagree with this. I think the library is impoverished and needs fleshing out to make C++ a stronger contender in the development arena.
What is already there?
A quick glance at cppreference.com shows the complete set of headers divided up into the following categories:
- Regular expressions
- Atomic operations
- Thread support
Some of these are more useful than others, and some attract more irritation than others. I know some developers who complain about the input/output, saying it’s bloated and inefficient. I know others who bemoan the incompleteness of the thread support. I don’t know anyone who actually uses the regular expressions, nor the localisation, nor std::multimap.
Then again, who doesn’t use std::vector, std::array, std::iterator, std::find_if and so on? There is plenty to choose from, and some parts find greater application than others.
What could go in the standard library?
One argument is that the standard library should be minimal and complete. It should contain types that require compiler support, like the type traits, and things that are strictly platform specific, such as SIMD support, file IO and atomic operations. The rest is just holding the user’s hand. I’m going to call this level 0 for want of a better description.
If you asked a user to write a mutex, I’m pretty sure they would get it wrong several times before they succeeded. Most likely they would not actually get it completely right and would encounter a peculiar edge case that broke everything at an inopportune moment. It suggests another class of content: things that are hard and error prone. This obviously includes thread support and also memory management. I’ll call this level 1.
Then there are the obvious types that it would be silly to do without. These are the types that are frequently reinvented and are the basic staples of many programs. These are the types such as std::complex, std::array, std::function, std::string, numerics and so on. They aren’t necessarily hard to write, but they are extremely broad in application and it is a safe bet that they will be used by nearly every programmer who will be grateful to have them. This is level 2, and standard commentators will start to murmur a little, remarking on how std::string is an unnecessarily large interface that should be made up of non-member functions, and std::function shouldn’t perform allocations.
What’s left on the list? The STL is one of the wonders of the modern age as far as I’m concerned but it doesn’t fit into any of the prior categories. The containers, iterators and algorithms aren’t “obvious” types, and there’s no particular need to have them in the library, but they’re incredibly useful. This is level 3, and here is where standard commentators start to get quite restless. They might remark that these types and functions are really quite large and bulky, they soak up implementer time, and they should have remained in a separate library.
What do other languages have?
Hand on heart, I’ve been full time C++ for twenty years now, with very little exposure to other languages, so I can’t speak from much experience. However, when you look at Java and C#, you find absolutely huge library support “out of the box”.
The Java Class Library consists of, at first glance, support for UI, database connection, networking, sound, image manipulation, XML, CORBA, encryption, even hosting for scripting languages: the list goes on.
If you decide to open Visual Studio and start writing a program using C# you have the Base Class Library (BCL) at your disposal along with the .NET Frameworks (FX) which together include support for networking, reflection, threading, XML, and parallelism amongst others. There is tooling for building GUIs too, and of course there is LINQ.
These languages are made a little differently from C++ though. Java is owned by Oracle, C# is owned by Microsoft, but C++ is owned by no corporate entity. The development of C++ is controlled by a public process which makes it rather slower to grow. Meetings to discuss, and vote on, what should be added and amended must be inclusive and public. However, there is an enormous wealth of support from these languages that would fit in at level 3, and it seems obvious to me that this support contributes to the popularity of those languages.
Having said this, it’s important to note that bigger is not necessarily better. I don’t think it is controversial to remark that these frameworks err on the large side. However, there is a useful core which I contend is larger than the standard library. That useful core drives the success of these languages. There is clearly a “critical mass” that can be achieved, and while those languages have erred on the other side of that mass, C++ has yet to reach it. I would rather we overshot a little and reaped the benefit of greatly increased adoption.
What typical jobs can’t be done “out of the box”?
Consider my opening anecdote of a Christmas in 1970s Great Britain. A more adult version of this is experienced by many learners who try and solve programming tasks using C++. Would you like to open a socket? Nope, can’t do that. Decompress a stream of data? Sorry, no. Open a dialog box? No. How about telling me what pixel my mouse is pointing at? Ummm… no. Flash some LEDs? Play a sound? Turn off the power? No, no and no.
It’s still the 1970s in C++ land.
What do we do instead?
Your toolchain will come with bindings for interfacing with the host operating system, which should solve network, power, sound and UI issues.
There is also a huge ecosystem of libraries. It really is impressive looking at the amount of code out there for people to choose from to solve your problems. Boost contains an Aladdin’s cave of riches. GitHub is overflowing with niche solutions for all manner of problems.
However, this tyranny of choice is paralysing for newcomers to the language. Operating system bindings differ for each OS. A fresh user cannot sit down with one book and any toolchain and learn the language through solving some real world problems: there is other, platform specific stuff to know.
Not only that, but not all code shops will permit the use of third party code beyond a language and its library. There are commercial, industrial and military environments that insist all code is created in a “clean room” environment. The arrival of software patents has made this an even more complicated matter.
Is there another vehicle for larger types?
As I said, I have skin in this game. I think a standard 2D API will be a great benefit to the library and I have one that’s nearly ready to go. I am keen to turn it into a Technical Specification and then see it incorporated into the International Standard.
But is there a destination other than the IS where it might be published?
At the moment, no, there isn’t, but I can envisage a repository of source libraries that have been “blessed” by the committee which exist outside of the standard. At the moment the committee consists of two streams, the library and the language. A third stream could be introduced which reviews candidate libraries for inclusion in this repository, rather than being added as level 3 contributions to the International Standard.
This repository could come with clear rules:
- All conforming implementations would have to be able to build it entirely.
- Contributions should not be burdened by licensing restrictions.
- Updates to the source should be reviewed by the committee.
What I’m talking about is a package index, or better still, a package manager. The former will retrieve libraries, while the latter will manage dependencies and auto-updates. If you’re writing some code in a blog post that uses graphics, you should be able to instruct your readers with something like
To build this code, type "cpp-get lib-graphics 1.56" at the command line.
and it should Just Work. It means we can assume every developer has access to a particular library.
There are already several developers looking at ways to implement a package manager. There are some mature efforts out there. Conan, apt-get and vcpkg are a godsend: indeed, the 2D graphics paper’s reference implementation relies on vcpkg to install the support libraries. But, and it’s a big but, we don’t have a standardised package manager or index, like Python’s PyPI (over 120,000 packages!), ready to power any C++ implementation with the extra voltage needed to write truly useful programs out of the box.
In summary, perhaps there are two ways to ship libraries that can be assumed to be accessible to all C++ developers:
- In the box (i.e. in the International Standard)
- In a package index (To Be Designed)
I hope that at some point in the near future something will emerge blinking into the light of day and we can examine an additional way of growing the standard library.
This post started as a pair of email conversations. One was with Titus Winters, chair of the Library Evolution Working Group. Several portions are lifted with gratitude directly from his words. The other was with Herb Sutter, chairman of the ISO C++ Working Group, and Richard Smith, C++ project editor, to whom the same thanks are directed. Many thanks also to Herb for review commentary and corrections on the organisation of C#. Remaining errors are of course my own.