This article attempts to capture the fundamental principles that
underpin the "why" of the various design choices that I make within the
reactive embedded system domain.
Although there are no absolute rules when it comes to design choices
(there is a time and place for every technique,) these principles serve
as a foundation for decision making against which choosing an
alternative must be carefully considered and justified.
Dynamic Memory
Most embedded software people agree that dynamic memory alloction
(e.g. malloc/free, new/delete) is an inappropriate technique to be
used in a system that must continue to execute indefinitely. The article
Memory Management In Embedded Systems
discusses this more completely.
Reactive embedded systems can be very dynamic, and while traditional
dynamic memory functions are convenient, they are inappropriate for most
embedded work. Dynamic memory management is best done explicitly using
techniques specifically designed for a particular type of application.
This may appear more cumbersome, but there are common patterns and techniques,
which become second-nature with practice.
Static Compile-Time Checking
Taking advantage of the ability of a toolchain to check software
prevents many defects that can otherwise be difficult to find.
By using specific types and refraining from quick-and-dirty techniques
(e.g. unnecessary casting) programmers/designers can employ the help
of the toolchain to create highly reliable software.
Practioners should
strive to express the conditions and invariants of an interface using
the appropriate facilities of the language.
- Use specific types.
- Use appropriate qualifiers such as const.
- Use references instead of pointers whenever possible.
- Use references instead of pointers whenever possible.
- Use static memory to take advantage of link-time memory limit checking.
Sooner Than Later Run-Time Checking
There are times when system limits cannot be checked by the toolchain.
When this happens, it is a good practice to expose any resource limitations
during run-time initialization. In other-words, pre-allocate the resource
in "main()" and produce an error at startup rather than waiting for the
error to come up during run-time, which may remain hidden until an infrequently
excersized set of conditions are met.
Client Owns The Memory
Separate Creational Interfaces
Loose Coupling
Interface vs Implementation
Event Driven
Active Objects, Explicit Behavior via FSM, contrast with algorithm
based (e.g. compiler), and GUI applications.
Client-Server Relationships
Client-Server Active Object Notation