Object-oriented C is simple
Although most C programmers aren't proficient in C++, they can still use simple object-oriented practices. Here's part one in a series of blogs presenting simple ways to mimic object-oriented practices in C.
I'm a strong proponent of using C++ to write embedded systems firmware. The facilities C++ brings to bear enables developers to create highly-portable object-oriented code. One of the great benefits of C++ is that it enforces rules that require the writer to navigate in object-oriented channels of thought and action. Without the use of object-oriented (OO) practices, over time you will lose clear lines of independent behavior. The result will be hidden interdependencies of subsystems that should only be known to one another through defined public interfaces.
So what about C? You can use C in ways that mimic C++'s principles and to some degree enforce OO constructs. In C, you don't have the protection of the compiler. To use OO practices in C, therefore, you need to create a set of rules and play by them. Choose your rules carefully--the rules you follow can make your job harder or easier, especially when debugging. Unlike some rules that require a bit of detective work, a violation of the rules I present here will be as clear as day to spot even for the casual observer of the source.
The concept of using OO practices in C is one of my favorite topics. Techniques for adapting OO to C can be simple to learn and elegant to implement. With this blog, I'll start exploring some of the simple ways we can achieve OO practices in C.
Tip: Keep it simple. How often do you look at code that has solved a complex challenge and say to yourself, "Wow, how simple." If you can master the art of taking a complex problem and creating a simple, elegant solution, you'll be revered by every firmware engineer who will ever have to support or use your code. To me, simple should always be the goal. Always.
So let's consider the concept of public and private attributes and methods. In C++ you get encapsulation and abstraction with compiler enforcement. What prevents us from having this in C? Well, the C compiler has no concept of encapsulation and abstraction, so we have to adhere to some simple rules to get the same results. Here are the first two rules:
Rule #1: Place public declarations of functions and variables in a separate file from those that are private to the module.
Rule #2: Only allow the use of headers from public declarations in the #include directive.
Let me elaborate with an example.
Say your product contains one motion sensor (we'll cover multiple instances in a later blog). What might we want to keep private about the sensor so as to maintain strong abstraction? Answer: the specific specifications of the sensor. What might we want to be public? Answer: motion detection. Did we detect motion or not?
So how do we easily enforce separation? Rule #1 and #2 and…
Rule #3: Embed in the file names whether the content is public or private.
One simple way to enforce Rules 1 and 2 is to embed in the name of your files whether they are private or public. Immediately, it becomes loud and clear if you're violating Rule 2 whenever you look at your list of file includes.
Public Source File: MotionSensorPublic.c Public Header File: MotionSensorPUBLIC.h Private Source File: MotionSensorPrivate.c Private Header File: MotionSensorPRIVATE.h
This is just one method. I've also used other naming conventions as well as just separating the headers while leaving the functions in one file. All that matters is that you follow these simple rules. As a result, a violation of Rule 2 will stand out like a thorn on a rose bush if you include a private header:
// // File Name: FilenamePrivate.c // File Description: Some description of the file's contents. // Author: Who to blame. // #include "PwmPUBLIC.h" #include "SpiPUBLIC.h" #include "LedPUBLIC.h" #include "SensorPRIVATE.h" #include "WatchdogPUBLIC.h"
So tell me you can't find the rule violation? Clearly one of these things is not like the other.
I've touched on a simple and easily enforceable set of rules that can be applied to your firmware's file-naming convention with the goal of creating object-oriented C. Notice the simplicity of it. That's always got to be the goal. These rules are like E=MC2-- simple and simply enforceable. Much more to come on this fascinating topic so stay tuned and I'll see you in the trenches.
Robert Scaccia is a firmware consultant and president of USA Firmware, LLC. He is very active with a large network of firmware consultants throughout the United States and Canada, is chair of the IEEE Cleveland Computer Society, and founded the largest regional, as well as international, firmware group on LinkedIn. Email him at firstname.lastname@example.org, or visit www.firmwareplanet.com or www.usafirmware.com