Welcome to the Modern Embedded Systems Programming course.
My name is Miro Samek and in this lesson I'll continue
with OOP and polymorphism, but this time you will see how
to implement polymorphism in portable, standard-compliant
This should reinforce what you've learned about virtual
functions in C++ in the last lesson and expose some additional nuances
of the VPTR-VTABLE implementation.
This lesson will also provide some general principles
and guidelines about using object-oriented programming in embedded
By the way, this is a very round lesson number hex-20, which
coincides with the even more round number of subscribers to the
Quantum Leaps video channel, which is about to cross hex-8000 or 2-
I'd like to thank you all for your help in reaching these
As usual, let's get started by copying the previous lesson30--the C
version without the underscore-CPP suffix, to lesson32.
Get inside the new lesson32 directory and double-click on the project
lesson to open it in the micro-Vision IDE.
To remind you quickly what happened so far, in the last lesson, you
learned about the OOP concept of polymorphism, which is the ability
to provide different methods for the same inherited operation in the
subclasses of a given class.
Specifically, you saw how polymorphism is implemented in C++ by means
of virtual functions and you have reverse-engineered the late-binding
mechanism, where the specific method for a given operation is
resolved at runtime based on the type of the object, not the type of
the pointer used in the call.
But before you go any further, from the last lesson is should be
clear that unlike class encapsulation and single inheritance, which
were essentially free in C, polymorphism in C will add coding
complexity and overhead.
Therefore, if you intend to use polymorphism extensively, you would be probably
better off by switching to C++.
However, if you build or use software libraries (such as the QP/C
real-time framework), the complexities of polymorphism in C can be
confined to the library and can be effectively hidden from the
But either way, this lesson�s primary goal is to show you how things
really work under the hood, which should help you to use polymorphism
more efficiently and with greater confidence in any language.
So, today you will implement the virtual functions manually in C
following exactly the C++ VPTR-VTABLE design.
For this, you need to explicitly add the VPTR to the attribute
structure of the Shape base class.
The VPTR will be the first attribute, and will be a pointer to the
'const' ShapeVtable structure.
This 'const' will allow the VTABLE to reside in ROM.
Please note that at this point you have not provided the declaration
of the ShapeVtable struct just yet.
But here you are using only a *pointer* to this struct, which the compiler
accepts, because all it needs to know for processing the Shape attribute
structure is the size of the pointer, which is known, not the
size of the whole VTable.
But now, you obviously need to declare the VTABLE, which even though
it's called a "Table", is typically not implemented as an array but
rather as a structure of pointers to all *virtual* functions, such as
draw() and area() in the case of the Shape class.
I used pointers to functions in this video course before, but today
is the time to introduce them a bit more carefully.
So, the C language allows you to provide a pointer to a function,
just like you can provide a pointer to a variable.
In both cases, a pointer contains the address (of the function
or variable, respectively) and also the type information
about the entity that is being pointed to.
In case of pointers to functions, the type information consist of the
full signature of the function.
So, to declare a pointer to the draw() function, you first need to
write the signature of this function.
And then, you need to convert the actual name of the function into a
pointer, which means using the asterisk operator.
But because of the specific operator precedence rules in C, the
asterisk would be bound to the return value instead of the function
so you need to add explicit parentheses around the pointer, like
You apply the exact same steps to declare a pointer to the area()
OK, so the VPTR and VTABLE are both declared, but now the users of
the Shape class as well as its subclasses must be able to call the
You can provide this virtual call functionality, also known as late
binding, in a few different ways.
First, you can provide member functions for that, just like all the
other operations of the Shape class.
Specifically, you can take the draw() method signature and turn it
into the Shape_draw_vcall() function.
Similarly, you can take the area() method signature
and turn it into the Shape_area_vcall() member function.
The implementation of these virtual call functions goes to the
Here, in the Shape_draw_vcall() function you first get from the "me"
pointer to the vptr.
Next, you get from the vptr to the specific pointer to function, such as draw.
And finally, you need to provide the parameters of the call, which include
the "me" pointer and potentially other parameters included in the
signature of the function.
You repeat the exact same steps for the Shape_area_vcall() function,
except that this function returns a value, so you need to add the
return statement in front.
As you can see, the C compiler accepts this syntax without
complaints, because the presence of the parameter list following the
pointer to function tells the compiler that this is a function call
based on this pointer-to-function.
However, this syntax fails to show you that you are de-referencing a
pointer and I prefer to be very explicit about it.
Therefore I prefer the alternative syntax analogous to de-referencing
any other pointer, that is, with the asterisk in front.
Of course, again, just like in the declaration of a pointer-to-function,
you need to use paretheses around the pointer.
As you see, the compiler accepts this version just as well.
But, either way, the most important point here is that the "me"
pointer is used *twice*: once to find the VPTR within the object, so
that the call is specific to the object, not to the type of the �me�
pointer; and the second time the �me� pointer is used as the usual
first parameter of a member function.
So, this is the first clean and straightforward solution that will
But it has a big drawback, and that is the additional function-
call overhead to call the invented vcall() functions.
But, the good news is that the newer C99 language standard allows you
to avoid this overhead, by using the �inline� functions introduced
exactly for this purpose.
To make the functions inline, you move the whole definitions of the
functions into the header file and you add the keywords inline and
static in front.
The discussion as to why it is a good idea to use both inline and
static is a bit lengthy and I�m going to leave it for another day, as
it is off topic for today.
But now, when you try to compile this code it fails, because as it
turns out, this compiler still works in the older C89 mode and does
not recognize the �inline� keyword.
You can remedy this, by opening the Project Options dialog box, and
going to the C/C++ tab, where you can tick the box next to C99 Mode.
As you can see, this triggers the complete re-compilation of the
whole project, but this time the code builds cleanly.
While the inline implementation of the virtual call mechanism is the
preferred way, for completeness I�d like to mention the third
implementation option based on preprocessor macros, which will look
You take the function definition and turn it into a macro by
stripping away most of the type information.
This is because macros provide only purely textual substitutions.
Therefore, to avoid any surprises, when for instance the client code
would use expressions for the macro parameters, it�s always a
good idea to enclose the macro parameters, like the �me� pointer
here in additional set of parentheses.
For all these reasons, macros are not nearly as good as inline
But I still show you the macros, because they are the only
low-overhead option for the older C89 compilers, which are still in
Alright, so you�ve seen how to define and how to de-reference
pointers to functions, but you are not quite out of the woods yet
with your virtual functions in C.
You still need to somehow define the VTABLE in ROM and also you need
to setup the VPTR in each instance of the Shape class and its
As you remember from the reverse-engineered C++ implementation from
the last lesson, the VPTR and VTABLE setup occurs in the constructor,
so that�s where you need to go next.
Here, you need to define an instance of the struct ShapeVtable in
ROM, meaning that it will be both �static�, that is not on the stack,
Now, a �const� object cannot be changed, so you must immediately
initialize at the point of creation.
But before you can initialize the vtable, you need to provide the
functions, with which to initialize the draw and area pointers-to-
functions in your VTABLE.
So, let�s provide the prototypes for the functions: Shape_draw() and
You can make these functions static, because they will be only used
locally inside the shape.c module.
So, now you can finally initialize your vtable instance, whereas you
can choose between the two options of the syntax.
First, you can directly use the function names.
The compiler will accept this, because the absence of parentheses
after the function names means that these are not function calls
but rather addresses of the functions.
However, I prefer the alternative syntax, in which you explicitly use
the ampersands, that is address-of operators, to leave absolutely no
doubt that here you mean addresses of the functions.
So, now, you can setup the vptr in the newly constructed Shape object
to point to the vtable for the Shape class.
And finally, you need to define the Shape_draw() and Shape_area()
Interestingly, at the level of the Shape class, you cannot really
provide meaningful implementations, because Shape is too abstract.
So, for now you just leave the functions empty, but to avoid compiler
warnings about unused parameters, you can use the idiom of casting
the unused parameter on void.
At this point, you are done with the general virtual function
machinery in the Shape base class, and you can immediately put it to
good use by writing the generic drawGraph() operation discussed in
the last lesson.
So, here is the C++ code from lesson 31, where an array graph holds
pointers to Shape, but these pointers actually point to subclasses of
Shape, such as Rectangle, Circle or Triangle.
The central point here is that this C++ code applies polymorphism to
call the specific draw() method for each Shape pointer in the graph
array in a very intuitive and elegant way.
So, now you your job is to do the same in C, using the virtual call
mechanism you just implemented.
Well, it�s actually very similar to the C++ version.
The only difference now is that you use...
the Shape_draw_vcall() inline function to apply polymorphism.
So, this is all there is to it, except that you obviously still need
to provide the prototype of the drawGraph() operation in the shape-
dot-h header file.
OK, the Shape base class is done, but some work is still needed in
the subclasses of Shape, like Rectangle or Circle.
They will all inherit the VPTR and the virtual call mechanism from
But, each subclass, like Rectangle, still needs to provide its
own VTABLE and its own specific implementation of the virtual
functions draw() and area().
This was the main purpose of polymorphism.
So, let�s do it now for the Rectangle subclass of Shape.
You need to go to rectangle-dot-c and pretty much repeat the steps
you did for the Shape base class.
So first, you add the static and const virtual table inside the
At this point, I presume that Rectangle does not add any new virtual
functions to the ones already specified in the Shape superclass.
In that case, Rectangle can use the ShapeVtable
Otherwise, you would need to apply inheritance in C with respect to
VTABLES, but let�s keep it simple here and not add any new virtual
functions in Rectangle.
So, back to the initialization of the VTABLE, instead of Shape�s
methods, the Rectangle VTABLE will obviously contain the Rectangle�s
But now, Rectangle already provides its own Rectangle_draw() and
The problem is that the �me� pointers in
the signatures of the Rectangle implementations are of type
while the pointers-to-functions in the Shape�s VTABLE have signatures
that expect �me� pointers of type Shape.
Because remember, that even though Rectangle and Shape are related by
inheritance, the C compiler doesn�t know about it and will not preform
So, to make the Rectangle signatures fit into the VTABLE originally
defined for the Shape base class, you need to cast pointer-to-
functions, by using the whole function signatures, like this.
Only now, you can finally assign the inherited VPTR for the Rectangle
class, but you need to be very careful to do it *after* calling the
constructor of the Shape superclass.
This is because, just like in C+ +, the Shape constructor sets the VPTR to
point to the Shape�s VTABLE, but here you want to override this
setting to the Rectangle�s VTABLE.
And this is all, because rectangle-dot-c already contains the
implementations of the Rectangle_draw() and Rectangle_area()
The very final touch, which will allow you to test all this in the
debugger, is to call to the generic drawGraph() operation that makes
use of the virtual call mechanism.
For this, you can open the C++ code from the last lesson and copy the
relevant snippet to your main-dot-c file.
In the C version, you need to setup the graph array of pointers
slightly differently, because you don�t have the Circle class in C,
so let�s use the s1 Shape object instead.
Also, instead of creating a Shape object dynamically, let�s create a
Then, of course you need to call the right constructor on it as well.
So this is about it, but to make the C compiler happy, you still need
to add an explicit cast in the Rectangle constructor and in the
initialization of the graph array of pointers.
Alright, so you got the code to build cleanly.
Now I�m sure you are curious how all this will work in a real embedded
So, let�s connect the TivaC LauchPad board and open the debugger.
The first thing I�d like you to verify is the Rectangle constructor.
When you step inside, the first code you see is the call to the Shape
constructor of the superclass.
Let�s step inside again and verify that the VPTR is initially not
set, but gets initialized to the Shape VTABLE containing addresses of
Shape_draw and Shape_area functions.
Also, please note that the VTABLE is definitely in ROM, because its address
starts with zeros.
Now, when you keep stepping, you return back to the Rectangle
constructor and there, the VPTR gets overridden to the Rectangle�s
VTABLE with addresses of Rectangle_draw and Rectangle_area functions.
So, your constructors in C work now exactly like the C++ constructors
from the last lesson, except that in C you had to add the VTABLE and
the code to set the VPTR manually, while C++ synthesized that code
Now, the most interesting part is to see the virtual calls inside the
generic drawGraph() function.
But before you step there, just note that the first object to draw will be Shape
s1, the second will be the Rectangle r1, and the third will be another
Rectangle pointed to by the pointer ps3.
Once inside drawGraph, step to the Shape_draw_vcall() virtual call,
and take a look at the disassembly.
When you compare it to the code generated by the C++ compiler, you
can see that they are... identical.
So, let�s step through this late-binding code a single instruction at
The first LDR fetches the VPTR from the me pointer in R6 and places
it in R0,
The second LDR fetches the first virtual function from the VTABLE now
in R0, and places it in R1.
The MOV instruction copies the �me� pointer in R6 to R0 to prepare it
as the first parameter of the call.
And finally, the BLX R1 instruction executes the call to the address
And indeed, you end up in the Shape_draw() method, because as you
remember the first pointer in the graph array was the s1 Shape class
The second time through the loop in the drawGraph function the same
late-binding code now invokes the Rectangle_draw function, however.
So, as you just saw, the C implementation of virtual functions works
exactly like the C++ original, down to the machine instructions for
At this point, I�d like to note that the VPTR-VTABLE implementation
of polymorphism in C that you learned here is not the only
In the literature or online you can find plenty of other
Perhaps the most frequently used alternative is to remove the VPTR
level of indirection and embed the whole VTABLE inside every object.
The advantage here is a little simpler virutal call as well as a
nicer syntax more closely resembling C++, because the draw() method,
for example, is invoked on on object using the dot operator, like
But the drawback is the increased RAM usage, because VTABLE is now in
RAM rather than ROM.
Not only this, the VTABLE is repeated in every object, so if you have many objects you can
easily double or triple your RAM usage.
An example book that presents this implementation method is �Design
Patterns for Embedded Systems in C� by Bruce Powel Douglass.
You can find there the whole chapter on object-oriented programming
and specifically its C implementation.
The author discusses Classes, Objects, Polymorphism and Virtual Functions,
Subclasses and other aspects.
There is also specific C code where you can clearly recognize the
VTABLE embedded directly in the attribute structure.
Interestingly, please note how this book also uses the �me� pointer
naming convention for the class member functions in C.
The last subject I want to touch upon today is when to use
polymorphism and perhaps even more importantly when not to.
Let�s start with a simple �code-smell� indicating that polymorphism
might be helpful.
For that imagine how the generic drawGraph() function would be
implemented in a very traditional C-code, without late-binding.
Well, it would probably look something like that:
Specifically, you would probably add an attribute �kind� into the
Shape structure, and you would also provide an enumeration of all
possible kinds of shapes,
such as RECTANGLE, CIRCLE, TRIANGLE, etc.
Then every time you would need behavior specific to the kind of
shape, you would use a switch or if-then-else branching to invoke the
right function for the kind of shape you happen to be dealing with.
But such code is far less maintainable, because every time you add or
remove a new kind of Shape, you would need to find and change all
places throughout the code.
In contrast, the virtual call mechanism is not only much more
It is also automatically extensible, because you can keep
adding and removing different shape subclasses, but you don�t need to
change the code with the virtual call at all.
You don�t even need to recompile the whole Shape base class, including
drawGraph() and any other functions like it.
So, here is your main guideline.
Whenever you see or anticipate code like the switch statement scattered throughout
your project, you should consider polymorphism.
But please remember that the only valid reason for applying
polymorphism is that the object-specific behavior needs to be
selected at runtime.
Which brings me to the guidelines when NOT to use polymorphism.
Well, you don�t need polymorphism when the selection based on object
type does not need to happen at runtime.
The most frequent misuse of polymorphism I see in the industry is in
attempts to manage product lines of related, but slightly different
For example, a medical company making, say, infusion pumps for drugs
might start with a single pump, but then keeps comming up with ever
more versions for various drugs and market segments.
The software for the new pumps is almost never created from scratch,
but rather is continuously tweaked and adapted from the existing
But actually, the software for a new pump is not copied and
Instead, a single, ever growing code base is
extended to service all existing pumps.
And herein lies madness.
Developers litter the code with conditional logic, which quickly becomes unmanageable
This code makes decisions at runtime based on the product
type, version numbers and similar variables, while really any given
code set ends up in only one specific product.
Therefore, the selection of the product does NOT need to happen at runtime.
It can happen at compile-time and link-time.
So, even if polymorphism could eliminate much of the convoluted IF-
a better way is to design a clean Board Support Package (BSP)
interface and then provide different implementations, different BSPs,
for different products.
So, for product ABC, you would have bsp- underscore-ABC, and so on.
This is the use of abstraction, as I explained back in lesson 29.
Of course, there is much more to it than just a simple BSP
The effective management of product lines requires
careful *physical design*, which is the way you partition your code
into directories and files, such as header files and implementation
That way, you can build the final software for any given
product by combining various modules at *link-time*, rather than
using techniques like polymorphism at runtime.
The art of good physical design is very valuable especially in
embedded systems programming.
Unfortunately, the subject is not widely known or appreciated.
While tons of books talk about logical design techniques, such as OOP, very few resources
exist for physical design.
One notable exception is the book �Large Scale C++ Software Design�
by John Lakos.
This book explains *physical design* really well.
And this concludes this group of lessons about object-oriented
programming in embedded systems.
In the next lesson, I will go back to my overarching theme, which is
introducing the main trends that shaped the modern embedded systems
Specifically, I will start a new segment, in which you will learn
about the last big trend that went mainstream during the 1980s, and
that is event-driven programming.
If you like this channel, please give this video a like and subscribe
to stay tuned.
You can also visit state-machine.com/quickstart for
the class notes and project file downloads.