reactive systems programming

a collection of code samples in C++

Reactive Systems

This is a collection of code samples in C++ (C++17) which are helpful for programming real-time embedded and reactive systems. The code samples are deliberately kept short to show only the essence of the respective topic and should be expanded to personal needs. All samples are free to use with CC0 license.

*

Uncoupled communication between two objects which run in separate threads can be achieved through buffers. A buffer is placed between the two communicating objects and can store a single element. There are different types of buffers depending on the context in which they are used but they all share the same interface which supplies two functions: put and get. Three different variations have to be considered: fully synchronous: put and get both block the calling thread.

A channel is a thread-safe queue which uses semaphores to synchronize access. The underlying queue in this implementation is the C++ std::queue container. It uses two semaphores sem_freespaces to count the free spaces in the queue and avoid overflow and semsize to count the elements in the queue and prevent underflow. In addition a std::mutex is used to synchronize access to the queue. To enqueue an element to the channel the sem_freespaces semaphore is first decremented which will block on a full queue and then increments the semsize semaphore after adding the element to the queue.

A factory creates objects of a certain type. The factory takes an ID of some sort as an argument and creates the object which has been registered with the factory with the respective ID. This code sample shows one way to make a factory. All objects are stored in an interal (intrusive) list inside the factory together with their ID. To create an object given its ID, the factory searches the list for the ID and calls the copy constructor of the object to create a new object of the same type.

A buffer is used for communication between objects which run in different threads. A buffer wraps some shared memory or a unique pointer thereto. The buffer may be accessed by the two communication partners via reference to the buffer which is initialized via constructor argument. A buffer is a single block of memory for a message (or unique pointer) and messages are not queued. Check out the channel if you need communication via a message queue.

This examples shows, how loggers are usally written. There are a lot of loggers to download for your project. Boost also has a logger. Look at the #defines in lines 6 through 14. Here the global logging level is used to decide if output into the logfile should be made. Also the access to the logger - which is a Meyers Singleton - is made much easier for the application programmer, since it hides the toruous call the singleton instance.

The memory pool is really fast in allocating and deallocating memory (O(1)). For real-time systems this is the right choice. It works by preallocating a pool of memory blocks. These memory blocks must all have the same size. So this is the downside to live with: This memory pool will only provide memory blocks of a certain size. If you need another size: Simply make another memory pool with that other size.

The very traditional way to implement the observer pattern is by using polymorphism. You have to derive the class which contains your callback function from an observer base class, and you have to overwrite the function notify(). This is done in the first code C++03 sample you see here. Create a ConcreteObserver, derived from Observer, and fill in your personal notify function. The subject stores all ConcreteObservers in a vector. When something changes in the Subject (e.

Algorithms often need parameters which need to be adjusted while testing the program. Instead of writing all parameters in a .h-file which requires that the code has to be compiled after each change, we propose a method which allows parameter handling during runtime in a uniform manner. A basic requirement for this is, that the parameters may still be used in equations, calculations and conditions without having to deal with bloatware method calls.

Object-oriented semaphores are not part of the C++ standard at this point. However, it is possible to implement a semaphore class using a mutex and a condition variable both of which where added to the standard in C++11. For the implementation shown on this website the function names where derived from the POSIX semaphores. The post function increases the count of the semaphore and notifies a thread that might be waiting on the semaphore if its count is at zero.

Mapping Signal IDs to function calls The state machine pattern assumes, that function calls are made by each incoming signal. But what if the signals exist only as a number or ID? How is the ID transformed into a function call? Of course, you could simply write a switch case, converting the integer ID into a function call. But switch cases “smell” (look into Martin Fowlers book on Refactoring).

A singleton is a class which controls the number of objects to be created from this class. Most often there shall be just a single object from a certain type. The basic principle to reach this is to limit the access to the constructor, i.e. making it private. There is a static function which allowes a single point of access to the single object. Controversial discussion There is a controversial discussion if a singleton is good design.

Hierarchical State Machines Hierarchical State Machines (D. Harel) [4] are a common way to model behaviour of a software modul. Bran Selic [5] has extended the ideas towards real time modeling, introducing actors, ports and other components. GoF [2] have first proposed an implementation. M. Samek [1] has elaborated on the implementation. Here, I propose an idea to implement the change of states using the placement new operator. Nico Manske has made experiments with this idea in his Bachelorthesis [3].

Introduction to Rodney Brooks Subsumption Architecture: The subsumption architecture is a layered methodology for robot control systems. It consists of coupled components and their hierarchical behaviours. One component is superimposed on the other as single layers. Each layer disposes of an arbitration scheme which empowers higher layered behaviours to manipulate the input and output of lower behaviours by suppressing the input or inhibiting the output (see fig. 01). Also visible in fig.

How to stop a thread. It is a nice idea to create a functor (overload operator()) and pass a functor object to a thread. The operator()() (line 9) should generally contain a while loop (event loop)(line 10). To be able to stop the while loop from the outside, one can use a member variable of the functor, e.g. bool IsStopped or bool IsRunning (line 15). The loop would then look like this