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, in its second part, reveals which interface design options architects should be familiar with and how these can be implemented in the C and C++ programming languages – it presents implementation approaches through association, composition, facades, virtual interfaces, non-virtual interfaces, C++ templates, and more. CRTP-Pattern on.
Concrete interface implementation examples

Image 1: Basis for concrete implementation examples
The software subsystem Controller contains a class cController, which requires two counters – an UpCounter and a DownCounter. The software subsystem Counter provides the controller with the interface icCounter to work with these counters.

Image 2Overview of implementation approaches
The program code corresponding to the implementation examples is provided here.
Association without interface class
The cController class accesses two objects of type cUpCounter and cDownCounter directly from the Controller software subsystem via two pointers. This results in two dependencies (include paths) between the two software subsystems, Controller and Counter.
The association between cController and cCounter was intentionally avoided here, so that no virtual functions or function pointers are necessary, but at the cost of stronger coupling.

Image 3: association
Aggregation without an interface class
In contrast to the previous association example, here the `cController` class directly handles the instantiation of the required `cUpCounter` and `cDownCounter` objects. This instantiation occurs dynamically on the heap using `malloc()` in C and `new()` in C++. This achieves the fundamental idea of propagating (or "expanding") the created counter objects during aggregation. The use of the heap is prohibited in many embedded software development projects because, among other things, it is associated with the risks of fragmentation and is therefore unpredictable and not capable of real-time operation.
As with the application of association, two dependencies arise in this example when using aggregation. The use of virtual functions/function pointers was also deliberately avoided.

Image 4Aggregation
Composition without interface class
In the association and aggregation variants, object access is achieved using pointers (optionally with references). Completely foregoing pointers results in the application of composition.

Image 5: Composition
Here, the class cController contains objects of the classes cUpCounter and cDownCounter as members. The number of dependencies remains at two, with the coupling through the embedded objects being strengthened during composition compared to association and aggregation.
Facade pattern
The facade pattern offers the cController the interface icCounter, which already contains data and function implementations.
What lies behind the facade of icCounter is neither worth knowing nor visible to the user.
As with the previous examples, this implementation contains no virtual functions or function pointers. It only has one dependency (include path) and therefore loose coupling. The interface (facade) contains a counter and a limit value check object, which together implement the complete functionality of the facade.

Image 6: Facade pattern – Interface implementation
Interface class with purely virtual functions
A classic interface approach originating from the object-oriented world is the use of purely virtual functions (only declarations without implementations) within the interface. Furthermore, the icCounter interface contains no data.
In cController, the interface is accessed via a pointer/reference of the interface type. This pointer/reference must later point to an object of the interface-implementing classes.
In C there are no virtual functions, therefore the mechanisms of the C++ compiler must be manually replicated using function pointer tables.
What lies behind the interface is initially neither relevant nor visible to the accessing cController. Only when the pointers/references are initialized do concrete objects of cUpCounter and cDownCounter need to exist.

Image 7: Virtual Interface – interface realization
Interface class with not only purely virtual functions
This implementation variant is based on the C++ idiom Non-Virtual Interface (NVI). When implementing purely virtual interfaces, redundant program code sections typically result in the case of multiple implementations.
The Non-Virtual Interface idiom already implements this common code within the interface function. Only the small, variant parts of the type-specific implementation are declared as purely virtual functions (isInRange() and count_raw()) within the interface and are already called in other implemented functions (count()).
Only the two type-specific virtual functions isInRange() and count_raw() are individually implemented in the classes cUpCpointer and cDownCounter.

Figure 8Non-Virtual Interface
In the examples with virtual functions in interfaces and/or classes, the result on the target is a dynamic binding. This creates various overhead costs, which, however, are negligible in most cases compared to the benefits:
- Program memory (and compile time)
At compile time, the toolchain generates a VMT (Virtual Method Table) for each class that declares and/or implements one or more virtual functions. The linker/locator typically places this VMT in program memory. The VMT contains the function entry addresses for the class-specific function implementations. - Data storage and Duration
Objects instantiated from a class with virtual functions contain, as an additional, first attribute, the entry address into its class's virtual function module (VMT). The compiler adds this attribute automatically, and the constructor also initializes it automatically. - Duration
When a virtual function is called for a specific object via a pointer or reference, the function address is read from the VMT (Virtual Function Table) using the object's VMT entry address, and then the program jumps to this function address (-> one more indirection than when calling non-virtual functions). This is the functionality of dynamic binding. It thus allows the programming of dynamic polymorphism. The C++ toolchain executes this mechanism automatically. In C, dynamic binding can be manually replicated with slightly more programming effort.
Interface as a template parameter
The overhead and associated risks described in the examples with purely virtual and non-virtual interfaces can be eliminated by using template classes. The trade-off is the loss of dynamic polymorphism, which is not strictly necessary in many embedded software systems. This results in only one static polymorphism.
The interface accessor `tcController` receives the object/interface types `CounterA_T` and `CounterB_T` as template parameters, whose implementations `cUpCounter` and `cDownCounter` it wants to address. There is no longer a direct dependency in the program code between the software subsystems `Controller` and `Counter`. The indirect dependency arises from the template typing during instantiation. The specified types `cUpCounter` and `cDownCounter` must provide everything that the accessor `tcController` calls. If this is not the case, the compiler reports this as an error (not a runtime error!).

Image 9: Interface as a template parameter
In this interface design, the interface itself is not clearly visible and is only indirectly specified in the accessor through its calls. This problem is improved by applying the Curiously Recurring Template Pattern (CRTP) [3].
Curiously Recurring Template Pattern (CRTP)

Image 10: CRT pattern
The template class tcCounter and its functions represent the complete interface and can be called by the accessor tcController:
mobjCounterA.count();
mobjCounterB.count();
The interface function `count()` of the interface template class `tcCounter` calls the `count()` function of the implementing class `tcUpCounter` or `tcDownCounter` as a delegate via the template parameter `Realization_T`:
template void tcCounter ::count() {
static_cast<Realization_T*>(this)->count();
}
The template parameter Realization_T is already set directly during the inheritance of tcCounter in tcUpCounter and tcDownCounter:
class cUpCounter final : public tcCounter<cUpCounter>
class cDownCounter final : public tcCounter<cDownCounter>
Setting a template parameter with itself as the class type is called Mixed In designated.
Summary
The decision for the „right“ interface design always depends on the applicable software requirements.
Interfaces positively support the implementation of software quality characteristics such as reusability, portability, interchangeability, and extensibility. Interface concepts are a suitable means of fulfilling software design principles, e.g., loose coupling, externalization of dependencies, modularization, and achieving high cohesion.
A further concept related to and involving interfaces is Ports. A port thematically combines zero to infinitely many provided interfaces and zero to infinitely many expected interfaces and can be connected to other compatible ports.
Learn more in Part 1 This article covers everything about interface concepts and different interface types.
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

