Intriguing C++ 20 features for embedded developers – InformTFB

Intriguing C++ 20 features for embedded developers

Intriguing C++ 20 features for embedded developers

C is still the favorite programming language among embedded system developers, but there are quite a few of them who use C++in their practice.

Using the corresponding features of C++ , you can write code that is as efficient as the code of a similar application written in C, and in some cases it will be even more efficient, since it can be quite tedious for an ordinary programmer to implement some functionality in C, which is much easier to implement using the additional features of C++.

C++20 is the seventh iteration of C++, preceded by, for example, C++17, C++14, and C++11. Each iteration added new functionality and in doing so affected a couple of features added earlier. For example, the auto keyword in C++14.

Approx. transl.:

In C++14, new rules were introduced for the auto keyword. Previously, expressions auto a{1, 2, 3}, b{1};were allowed and both variables had a type initializer_list<int>. In C++14auto a{1, 2, 3}; , it results in a compilation error, and auto b{1};compiles successfully, but the variable type will be inta not initializer_list<int>. These rules do not apply to expressions auto a = {1, 2, 3}, b = {1};where variables still have a type initializer_list<int>.

Significant changes have been made to C++11, and it is the most common version of the standard that is used in embedded systems, since embedded system developers do not always use the most up-to-date tools. Proven and reliable solutions are crucial when developing a platform that can last for decades.

It just so happens that quite a lot of new functionality has been added to C++20. New iterators and string formatting support will be useful with the new synchronization library. Everyone knows the three-way comparison operator, also known as the “spaceship” operator. Like most functionality, the description of this operator is beyond the scope of this article, but in short, the type comparison x < 20will first be converted to x.operator<=>(20) < 0. Thus, comparison support for handling operators like<, <=, > = and <=, > > can be implemented using one or two operator functions, rather than a dozen. The type expression x == yis converted to operator<=>(x, y) == 0.

Approx. transl.:

For more information about the spacecraft operator, see the article @ViistominThe operator of the new spaceship (space ship) in C++20»

But let’s move on to more interesting things and look at the functionality of C++20 that will be of interest to developers of embedded systems, namely::

Constants compile-time

Embedded developers like the ability to do things at the compilation stage of the program, rather than at the execution stage. C++11 added the constexpr keyword to define functions that are evaluated at compile time. C++20 has extended this functionality to include virtual functions. Moreover, it can be used with try/catch constructs. Of course, there are a number of exceptions.

The new consteval keyword is closely related to constexpr, which essentially makes it an alternative to macros, which, along with constants defined via the #define Directive, are the Bane of C and C++.

Coroutines

Coroutines are often supported by compiler tools and some operating systems. Coroutines provide some form of multitasking and can simplify the implementation of state machines and other applications, such as a round-Robin scheduler. In fact, such a task itself transfers control (approx. transl.: cooperative multitasking) and then resumes its work from this point.

Approx. transl.:

For more information about coroutines and what they are used for, see @PkXwmpgN’s articleC++20. Coroutines” and in the answer to a questionasked on stackoverflow.

C++20 supports coroutine functionality with coroutine_traits and coroutine_handle. Coroutines are stack-independent and store their state on the heap. They can transfer control and, if necessary, provide the result of their work to themselves when coroutine execution resumes.

Concepts and limitations

Concepts and constraints were an experimental feature of C++17, and are now standard. Therefore, we can assume that the experiment was successful. If you were hoping for Ada and SPARK contracts, this is not the case, but the concepts and limitations of C++20 are valuable additions.

The advantage of restrictions is that they allow you to detect errors at the compilation stage of the program. Restrictions that may apply to class templates, function templates and ordinary functions. A constraint is a predicate. transl.: statement), which is checked at the compilation stage of the program. A named set of such definitions is called a concept. Sample code taken from cppreference.com, shows the syntax and semantics of this functionality:

#include <string>
#include <cstddef>
#include <concepts>
using namespace std::literals; 

// Объявляем концепт "Hashable", которому удовлетворяет
// любой тип 'T' такой, что для значения 'a' типа 'T',
// компилируется выражение std::hash{}(a) и его результат преобразуется в std::size_t
template <typename T>
concept Hashable = requires(T a) {
    { std::hash{}(a) } -> std::convertible_to<std::size_t>;
};
 
struct meow {};
 
template <Hashable T>
void f(T); // Ограниченный шаблон функции С++20
 
// Другие способы применения того же самого ограничение:
// template<typename T>
//    requires Hashable<T>
// void f(T); 
// 
// template <typename T>
// void f(T) requires Hashable<T>; 
 
int main() {
  f("abc"s); // OK, std::string удовлетворяет Hashable
  f(meow{}); // Ошибка: meow не удовлетворяет Hashable
}

Concepts and constraints are the most significant functionality of C++20, which is focused on creating safe and reliable applications for areas such as transport, aviation, and medicine.

Modules

The ubiquitous #include is being replaced with modules. The import and export keywords are located where #include was once located.

The #include Directive made compilers easier to write. When the compiler encounters it, it simply reads the file specified in it. In fact, it turns out that the included file is part of the original file. This approach depends on the order in which the files are combined. In General, this works, but it scales poorly when working with large and complex systems, especially given all the relationships that C++templates and classes can bring.

Module-based build systems are widespread, and languages such as Java and Ada already use a similar system. Basically, the module system allows you to load modules only once at the compilation stage, even if they are used by multiple modules. The order in which modules are imported is not important. Moreover, modules can explicitly grant access to their internal components, which is not possible when using the #include Directive.

Existing standard libraries will now support inclusion as modules, but for backward compatibility, support for the #include Directive remains. Modules work well with existing namespace support.

I still tend to program in Ada and SPARK, but the new changes in C++20 make it the best C++ platform for developing secure and reliable software. I actually need to work with the new C++20 features to see how they affect my programming style. I tried to avoid coroutines as they were non-standard. While the concepts and constraints will be a bit more complex, they will be more useful in the long run.

On the positive side, open source compilers support C++20, and there are more and more commercial versions of compilers that support C++20 on the Internet.

Valery Radokhleb
Valery Radokhleb
Web developer, designer

Leave a Reply

Your email address will not be published. Required fields are marked *