The use of software interfaces is a fundamental tool for developing durable and robust software architectures. Therefore, they should be established as early as possible in the architecture to stabilize it. This allows the software architect to ensure rapid task distribution to independent individuals, teams, or locations without further friction.
This article reveals which interface design options you should be familiar with and how they can be implemented in the C and C++ programming languages. The first part introduces interface concepts and different interface types.
The interface concept and design variations
A software interface represents a sum of functions with the complete semantics (name, parameters, parameter types, return types, special modifiers) available for access and implementation.
At least one element (accessor) accesses the interface. Accessor The interface expects (Required Interface).
At least one element (realization) must implement the interface. realization provides the interface (Provided Interface).
The interface serves to decouple the user from the implementation, and more broadly, to decouple architectural elements.

Image 1Interface concept
Different design variations between gripper and interface(s)
- When a user accesses one or more interfaces
- multiple attackers intervene in one or respectively an interface to
- multiple users access one or more interfaces
- multiple accessors access different interface levels or
- Several attackers seize the opportunity respectively different interface levels to
The following design variants are possible for interface implementation:
- an interface with an implementation
- an interface with multiple or partial implementations
- multiple interfaces in one or more implementation variants
Interface typing
Interfaces can be thematically divided (depending on the access point), for example, one for configuration, one for diagnostics, and one for normal operation per architectural element. Generally, interfaces can be categorized into three different types:
- The Call interface For example, it offers the accessor from architectural element A functions to read values from architectural element B, write values into it, or trigger algorithms there.
- The Callback interface Actively reports new values or events from architectural element B to A. Regarding the structure, it's important to note that the implementation of the callback interface spans architectural element boundaries and is located within architectural element A. In conjunction with the call interface, this still results in a... unidirectional dependency accessible between the two architectural elements A and B.
- If the callback registration is performed dynamically at runtime and not statically at compile time, a special element (manager) in the software architecture is responsible for this. This manager can be configured using a special Callback registration interfaces Register and unregister one or more callback-implementing elements.
To implement the structure shown in Figure 2, the following can be done: Observer pattern apply.
The concrete subject receives a new value. By calling a corresponding function from the observer, the concrete subject reports the new value to all previously registered concrete observers. The observer pattern has since become a very popular pattern for embedded software.

Image 2: Callback structure with registration interface
The semantic interface and the functions
The interface typing is reflected in the assigned <Stereotypes>> and in the interface names as Prefix ic (interface), icb (interface callback) and icbreg (interface callback registration). As Interface name A meaningful and expressive noun is suitable.
The functions are available in all interface types. public (public).
Functions are programming language dependent. Modifier such as virtual, const, static, inline, … to be taken into account.
As Function names Verb/noun combinations are suitable. For callback interface functions, the verb "melde" (notify) is appropriate. For callback registration interface functions, the verbs "registriere" (register) and "de-registriere" (unregister) are appropriate.
Function parameters These parameters are optional and should not exceed a maximum of seven to twelve. The direction can be visualized by using `in`, `out`, or `inout` before the parameter name. If the programming language (e.g., C++) supports a default parameter value, this can be specified.
The order of function parameters can affect performance. It's advisable to start with standard data types, as the compiler can pass these to the function via CPU registers (provided there are free registers). Once a complex data type, such as a structure, is passed by value, the compiler passes this and all subsequent parameters to the function via the stack.
As Parameter name A noun or a combination of several meaningful and expressive nouns is suitable.
parameter- and Return types are optionally void. Data types can be either standard data types from the programming language or custom data types that have already been defined, but not data types that will be defined in the future.
For performance reasons, passing a pointer/reference (not a copy!) is always preferable to passing a value (copy!).
Data type names should be identified by a postfix _t.
As with functions, types also have programming language-dependent modifiers such as const, static, *, &,[], … .
Approaches to interface implementation
In its simplest form, an interface is a header file containing a collection of declared functions. The accessor includes the interface header file to call the interface functions. One or more implementing modules include the header file to implement the interface functions. This simplest implementation approach will not be considered further here. Instead, we will focus on more advanced implementation options in C++ and, where feasible, also in C.
The interface designs presented at the beginning can be implemented using the following approaches:

Image 3: Implementation approaches
Not-polymorphic structure This means that the interface has exactly one implementation, while polymorphic structure implies the presence of multiple implementations. With polymorphic structures, the type of implementation can be determined. binding Distinguish between object/function pointer and function. Dynamic Binding (runtime binding) allows for context-dependent calls to different interface function implementations. In the case of static Binding (compilation-time binding) means that the compiler binds an interface function implementation that cannot be changed at runtime.
The different implementation approaches can be evaluated as follows:

Image 4: Evaluation of the implementation approaches
The choice of the right approach depends heavily on the software quality requirements that need to be met. If high runtime flexibility is required, then polymorphic structures with dynamic constraints are the right choice. If functional safety takes precedence over flexibility, then non-polymorphic structures or polymorphic structures with static constraints are preferable.
The second part This article presents implementation approaches through association, composition, facade, virtual interfaces, non-virtual interfaces, C++ templates, and CRTP-Pattern on.
Gain the right knowledge about which interface design options you should be aware of and how these can be implemented in the programming languages C and C++.
MicroConsult offers you professional Training and coaching all about the topics Analysis, design and architecture and much more – also in live online format.
Further information
Training courses on the topic – also available in live online format:
- Requirements engineering and management for embedded systems
- Software architectures for embedded and real-time systems
- Embedded C++ for Advanced Users: Object-Oriented Programming for Microcontrollers with C++/EC++
- Embedded software design and patterns with C
- Interface design – analysis, design and architecture

