A modern elevator is a complex piece of technology. As well as a modern car, it is a distributed system comprised of multiple control units (0), some with many functionalities.
A control unit’s software needs to perform fine-grained control of the hardware and, at the same time, express high-level application logic. In some cases, it might also need to handle connectivity, for instance to other units or external entities.
Moreover, the overall system has strict requirements for security and safety. The designer also needs to keep hardware costs and - to some extent - power consumption under control.
It is necessary to clarify that such a complex system cannot rely on a single framework or programming language. A modern elevator depends on a broad set of technologies.
But is there a single programming language to go from low-level hardware control to the abstraction needed by higher-level tasks?
A possible answer could be C++ (although some might argue that newer languages like Rust can achieve the same, while others will point to plain C as a better alternative, adding that you don’t need a "large and complicated language" (1) like C++).
Let's explore three features that make C++, in particular modern C++, suitable for developing the software of a modern elevator.
You do not pay for what you do not use
With the increasing complexity of the application logic, hardware capabilities for elevator control units are improving too. Anyway, we are still talking of a constrained system that needs to manage resources carefully.
C++ provides abstraction and high-level functionalities without imposing extra costs when those features are not needed.
This principle is referred to as "zero-overhead abstraction" or, in layman's terms, "what you don't use, you don't pay for" (1).
C++ maps its basic types directly to hardware memory entities (1). It does not have a big runtime such as a VM as in Java or Python, and it doesn’t have a garbage collector either.
Its generic containers are lightweight, too, thanks to the usage of templates in the standard library.
For example, a C++ std::array does not introduce overhead compared to a C-style array but provides many benefits: it prevents automatic decay into a pointer, maintains the array size, provides bounds checking, and allows the use of C++ container operations (3).
This example leads to the second part of the zero-overhead abstraction principle, which goes further with "what you do use, you couldn't hand code any better" (1).
In the std::array case, it simply means that if, for example, you want to use a basic integer array, there is no lower-level implementation of the array that could achieve better performances or memory usage.
Let's consider the std::count_if function (2). When calling it with non-const data, its assembly output is equivalent to the output of a C function manually written to handle a specific low-level data type. Moreover, when called with const data, it lets the compiler optimize, evaluating everything at compile time.
Choosing -Os as optimization level, the following code
or even better
gets compiled to
directly copying to r0 the number of occurrences of the number 8 in the data array.
Check it out yourself on Compiler Explorer: https://godbolt.org/z/o1nnPhEPY !
Clear interfaces: type safety and resource safety
The codebase of a modern elevator is huge, with many people involved in its development: defining clear software interfaces is crucial.
One requirement for a language to define clear interfaces is to be type-safe and with a rich type system.
Though C++ is not a strongly typed language, complete type safety is a guiding ideal for the language (4). Direct mapping to the hardware of basic types and compatibility with C make complete type safety something the language can only approximate.
Here’s what Bjarne Stroustrup (creator of C++) has to say on the topic:
Over the years, the set of language features, standard-library components, and techniques supporting that ideal has grown. Outside of low-level sections of code (hopefully isolated by type-safe interfaces), code that interfaces to code obeying different language conventions (e.g., an operation system call interface), and the implementations of fundamental abstractions (e.g., string and vector), there is now little need for type-unsafe code. (5)
While custom solutions could be adopted to achieve strong typing for high-level interfaces, easy-to-use libraries such as NamedType (6) (7) can help with this task.
Along with type safety, resource safety is critical for obtaining clear software interfaces.
How has the resource returned by that function been allocated? Is it necessary to free it after use?
Modern C++ supports the developer with smart pointers representing shared and exclusive ownership for dynamically allocated resources.
Furthermore, it makes it easier to avoid leaks thanks to RAII (Resource Acquisition Is Initialization) technique.
Just like type safety, even resource safety lives under the constraints of:
no restriction of the application domain compared to C and previous versions of C++; compatibility with former versions of C++.
These constraints make perfect resource safety an ideal to approximate.
To conclude, it is also worth mentioning that Bjarne Stroustrup and Herb Sutter maintain a set of guidelines for modern C++ to "emphasize static type safety and resource safety" (8).
Reliability and maturity
While compatibility with previous versions of C++ is undoubtedly a constraint, this at the same time highlights how long-term stability lies at the core of the language’s proposition. (4).
Its standardization by the International Organization for Standardization (ISO) (9) helps to support this claim. Why is long-term stability important for elevators?
Elevators usually stay in operation for decades. Updating their software using new language features without being forced to rewrite the whole codebase is a must.
Long-term stability, though, is not stopping C++ from evolving. We saw higher up how modern C++ introduced new features to better approximate its ideals of type and resource safety, for example. Even as a 40-year-old programming language, C++ does not look outdated at all: generic programming, lambda functions, type deduction, and concepts - these are just some of the features that have been improved or have become available over the past ten years.
A general-purpose language must evolve to keep a broad user base. And choosing a language with a broad user base is extremely important for a company producing elevators to hire a high number of competent engineers to work on its complex systems.
Is C++ the only and perfect choice?
In this article, we reviewed three reasons to choose modern C++ over other programming languages for the software of a modern elevator. Of course, many more could be listed. And, as for all technologies and tools, C++ as a programming language also has cons. For example, C++ is not an easy language to master, and its compilation process is not the fastest.
Moreover, software developed in C++ is still not 100% adherent to modern best practices in terms of memory management leading to use-after-free, double-free, and similar memory issues.
C++ does not claim to be the perfect language (11). But it is a practical language (10) whose tradeoffs must be understood before applying it in a specific business context (12).
Resources
0 - Hello Software World, Schindler Group article
5 - The C++ Programming Language, 4th Edition, p. 14