This is another in my occasional series of postings, where I give a few little tips that may be of benefit to the embedded software developer. I endeavor to address all aspects of embedded software: general programming, C, C++, system design and operating systems. Questions about and contributions to this series are always welcome by email or via social media.
Today I am looking at real time systems, programming philosophy, C++ operator overloading, C program structure and inter-task communication …
Do not confuse “real time” with “real fast”.
Real time means predictable or deterministic, but not necessarily fast. Although the term is commonly used to say that a system is fast enough to do a job, it would be equally accurate to use the term to describe a systems that was slow enough.
Always write code with the human reader in mind, not the compiler
Why do you write code? The obvious answer is to create a sequence of instructions that will be executed by the CPU. Although that is true, it is worth thinking about how developers really spend their time. Much more time is spent debugging and maintaining code than developing it in the first place. So writing readable code is more important than the last scrap of efficiency. A good compiler will generate efficient code from almost anything that you throw at it. So, think about someone trying to understand your code in the future and make their job easier [It could be you!].
If you overload an operator in C++, it is generally unwise to completely change its functionality
Operator overloading in C++ is a zero-cost way to write clearer code, if it is used properly. It enables you to perform operations on objects that are much easier to understand at a glance than member functions. However, you must be careful to ensure that the operation performed is the one implied by the operator. I am skeptical of the overloading of << and >> for input/output operations, for example. Also, when I was first learning C++ many years ago, the book I was using showed overloading of the + operator so that you could write a + b; to add b to a. I was mystified. Of course, the author should have been overloading += not +.
If you have a choice between if…else and switch, always choose switch
For making simple decisions, an if…else construct is fine. However, deeply nested structures can be very hard to read. If at all possible, redesign your code to use a switch which is easier to read/understand and gives a compiler the chance to generate very efficient code.
Never use a shared variable to communicate between tasks
In a multi-tasking application, data commonly needs to be passed from one task to another. A global variable might seem like a good idea, but it does not provide any access management, which can cause problems. The best practice is to use facilities provided by the RTOS, like mailboxes, event flags, signals or queues.