How to Get Started with C++ in Embedded Systems---Michael Wilk & Michael Barr
Michael Wilk: Hello, welcome and thanks for
joining us.
My name is Michael Wilk, I am an engineer
and consultant with Barr Group.
I have been using C++ for over 20 years now.
When using C++ I try to take a pragmatic approach
and I won't claim to know everything there
is to know.
I came to the world of embedded development
by way of desktop, client server and internet
application development.
Moving from those environments, directly to
doing embedded C++ made me start considering
codes based performance in RAM issues pretty
quickly as I am sure you can imagine.
Before we really get going here though, I
want to be sure to thank Dan Smith for helping
put this webinar together.
He has been a big help and I have really enjoyed
discussing using C++ with him.
Slide 5: Overview of Today's Webinar
Moving on, so the range of it experience in
the embedded C++ world is quite large.
Some developers use a very small subset of
the languages features.
Many embedded developers refuse to learn or
even consider using C++.
And honestly, sometimes it's not even an option
due to the processor or toolset that's available.
Unfortunately, C++ has developed a bad reputation
in the embedded community mostly undeserved,
but we will try to restore C++'s honor in
today's webinar.
We are going to try and give you a basic strategy
for starting to use C++ in an embedded environment.
We will be focusing on trying to dispel fears
and misconceptions about C++ by explaining
how it can benefit you in both the near and
longer terms.
Slides 6-10: Common C++ Misconceptions
Our first topic will be to dispel some of
the biggest myths and misconceptions about C++.
We have heard these myths and misconceptions
numerous times and we know that a lot of you
probably hold some of these beliefs
Starting with, the first one, C++ code is
always larger than C code.
No this simply is not true given the same
code, it is of course true that going unchecked,
you can have larger code.
A lot of this myth has survived from the earlier
C++ compilers in the early 90s that weren't
much more than macro expansions to implement
various C++ concepts.
Modern C++ compilers have come a long way
and are quite good at optimizing the generated
code on those if not all targets.
The most obvious thing you can do here is
run your own benchmark, compile some code
with C and C++ compilers with equal optimization
settings and see for yourself.
The next one is C++ code is always slower.
And this is probably at least partly related
to code being larger, expected to be larger
with more instructions, being generated by
the compiler to perform the equivalent operations.
There are some misconceptions that there is
a lot of overhead when doing anything in C++,
especially when using classes that take advantage
of inherent and certain things like that.
Our experience is that to achieve similar
or the same functionality you may actually
end up with faster and smaller code in C++.
This is of course not always true, but there
are definitely situations where C++ can be
quite beneficial performance, among its other
benefits.
The next item is that C++ always needs a heap.
This again is not true.
There are times we all say, this would be
so much easier if I could just use the heap.
I have been on projects where the memory is
set aside for the system heap was set to 0.
Dan told me about a project where they have
replaced malloc and it would throw an assertion
if somebody tried to allocate memory.
There are techniques similar to C where you
can specify exactly where you want to locate
an object or objects and that they can be
pre- allocated.
Further we can also use a stack to allocate
any needed objects and we are going to see
some examples of that today.
When you get started with C++, it is not mandated
that you even use classes especially dynamically
creative one.
So there is no reason why the heap has to
be used for the creation of such data structures.
I will be showing some examples of this in
just a little bit.
Next, nobody use the C++ for embedded, well
that's definitely not true.
Myself and Dan are just two people that view
C++ in numerous embedded projects, we have
clients here at Barr Group that uses C++ and
we have both been associated with embedded
C++ projects prior to Barr Group.
I have developed products for medical devices,
consumer electronics, industrial controls,
and other domains using C++.
I do know some infotainment systems and cars
out there that use C++ for their implementations
as well.
According to the recent Barr Group's survey,
about 20% of the respondents indicated that
they were using C++ in their embedded projects.
So let's put it this way, if C++ wasn't being
used for various embedded targets, the vendors
would likely not be even offering it as an
option.
The next item is, I have to know and use object
oriented design to use C++.
Well this actually can help a lot to take
advantage of certain features of the C++ language
but it's not absolutely required.
As we will discuss later you don't have to
nor should you start with some complex, multiple
inheritance, abstract, crazy, complex objected
oriented design.
As many of you probably know you can do that
in C as well.
You can use C++ in either an object oriented
or a non-object oriented manner just like
you use C. I have done things like this both
ways, I prefer C++ as it's a lot less work
and less prone to error when I am doing something
in a more object oriented manner.
We would like to emphasize that it's been
official to separate your design from your
programming language, given a design you should
choose the programming language that's going
to help you reach your goals.
Slides 11-14: Practical Benefits of C++
What are the practical benefits of C++?
Perhaps a better question is, since C is the
dominant language, what are its weaknesses?
The first item is Type Safety, by now most
of you probably know this C is not the most
type safe language in the world.
C++ provides much better type safety generally
checked and caught by the compiler.
Data structures are actually types in C++,
and this includes not only as classes but
structs and enumerations as well.
And so we will see there are better ways and
safer ways for checking and casting data types
in C++.
The father of C++ Bjarne Stroustrup had stronger
type safety as one of the major goals of C++.
Namespaces, C has no concept of the namespace,
if you have been around for a while; you have
likely experienced a name conflict with an
enumeration or a function or a variable.
Often you will see, off the shelf C libraries
will have a prefix on enumerations or functions
in order to avoid any kind of name conflicts.
In C++, we actually get namespaces as part
of the language itself.
This allows you to bundle or package things
into separate named region.
So a function maybe called InIt, in the display
namespace as a different function than a function
also name InIt in something like a networking
namespace.
Best of all, these come at no cost to you,
they are handled at compile time and incur
no extra cost in terms of code size or performance.
This is a really nice feature of the language
and something that most if not all, modern
languages provide.
Next is constructors and destructors.
C++ actually natively and implicitly provide
constructor and destructor functions.
As some of you or most of you may know, classes
and structures in C++ provide the ability
to implement a constructor and destructor
to initialize as well as tear-down objects
as needed or appropriate.
This has to be done manually in C, which is
an opportunity for mistakes.
The last item is object oriented semantics,
which I am not going to emphasize too much
here since earlier I said, you don't really
need to know object oriented design concepts
to use C++.
But a nice feature of C++ is the fact that
it does support object oriented semantics
as part of the language, as opposed to C which
does not.
Slide 15: How Do We Get Started?
So we have had discussions with clients that
have said, okay I can see the benefits of
C++ but how do I get started?
We have discovered that trying to take on
too much with C++ from the beginning is like
drinking from a fire hose.
If you bite off a big chunk right away saying,
everything is going to be done using every
possible feature of C++, you are bound to
have problems.
Here is what we suggest, have a strategy to
get started, some of this will depend whether
you are starting a brand new project with
a completely blank slate or if you have got
an existing product that you want to either
refactor or integrate some pieces in C++.
This is usually when somebody says, but wait
I have got a million lines of legacy C code
so I can't use C++.
Well, yes you can, that's another myth that
C and C++ don't play nicely together or it's
just a one-way street from C++ to C. You can
in fact call C++ code from C code using a
useful and simple technique that we will see
in just a couple of minutes.
Slides 16-19: Preparing for C++
Okay, so we have decided to proceed with using
C++.
Our first step is to prepare ourselves for
using a mixed language environment.
Our first step is going to be replacing C++
keywords in our legacy code.
This is likely your first step and things
like the words class, friend, virtual anything
like that, any keyword from the C++ language
you got to want to replace in any existing
code to make sure you don't get compiler errors.
Remember that bool is a keyword and actually
a type in the C++ language.
Chances are you have defined your own Boolean
type or you have used standard bool, you will
need to keep an eye on this one to ensure
compatibility and efficient type casting here.
Next, calling between C and C++ code, we are
actually going to show a couple examples here
of calling between C and C++ code.
However what I would like to say here is to
have a strategy of how and where calls between
these two are going to take place?
For example, is it okay to call directly into
legacy code from anywhere in your new C++
code or do you want some sort of wrapper?
A wrapper can be very beneficial because if
you factor the legacy code, any APIs that
have become deprecated, won't have to be replaced.
Everything is abstracted by a wrapper class
which may in fact become the implementation.
So next, calling C code to C++ that takes
a little bit more work and we are going to
show an example of that in just a couple of
minutes.
Our next item here is const correctness and
this applies to C as well actually.
This is generally considered a best practice
and if you are passing objects around as pointers,
determine if those pointers can be declared
as const especially in functions that use
but don't modify the data.
This can actually be a challenge, however
it can help ensure no strange side-effects
and catch problems at compile time.
As part of this, do not unless, unless you
are absolutely sure it's safe, castaway, constness
of an object.
And our last item here is refactoring your
C code into well-defined modules.
This is somewhat optional and I would consider
it more of an exercise and good software engineering.
Take the opportunity to break large and complex
code into manageable well-defined chunks,
each with a specific purpose.
This will help from a testing, debugging and
migration point-of-view, especially if you
migrate your code modules into classes later.
Doing this can help you later further migrate
and/or refactor legacy code into well-defined
and testable classes.
Slides 20-21: Calling from C++ (And Vice Versa)
So let's discuss calling C code from C++ as
well as calling from C to C++.
We need to do a couple of things in our C
code to ensure we have properly declared methods
on both side so we can call in either direction.
In a combined C and C++ program, we now have
a mixed language environment with more than
likely shared header files.
Not that true C++ header files can't be included
from C files unless you are using a C++ compiler
or if you are careful about how you use the
preprocessor.
A C-only compiler won't like C++ classes and
other data types that are really C++ only.
In our example, we have declared a couple
of functions to specify what's called C linkage
if the C++ compiler is being used.
Note the (_ _ cplusplus check) that's a standard
preprocessor macro which is defined internally
by all C++ compilers.
As you can see here the two functions, if
compile under C++ will use the extern "C"
syntax, this is telling the compiler that
we want these functions to have C linkage,
without getting into a lot of under the hood
details it compiles the C++ code in a way
that makes it accessible to a standard C program.
If you are interested in more information
on this topic, you can do a search on the
internet for the term Name mangling.
So here is our source code for our first example,
some of you may have grown when you see printf
being used but with the tool chain we are
using to create the webinar's content, this
is the trace mechanism that works well with
printf.
It also shows that we can still call, functions
from the C standard library from C++ code,
even though it's not the greatest or safest
thing to do, especially in an embedded target.
In this first example we have a very simple
program with the C entry point, main and this
calls into what we will call a C++ interface
function, in this case named cpp_if_function.
That function has C linkage as we showed in
the header file and access our gateway to
our C++ code calling more useful C++ code.
In our example, we are calling a statically
declared C++ function that uses default parameters,
a feature not available to C code.
This could actually use the C++ Object called
object methods, make use of templates etc.
The static method real_cpp_func is a C++ method
that in turn calls back to our C function
in our other compilation unit.
This fairly simplistic example demonstrates
a complete circle of C code calling C++ code
which in turn calls C code.
This is a very useful way to begin integrating
small pieces of C++ code into your code base.
Slides 22-28: The First Month
Okay, so now we have seen an example of how
to call between C and C++ and hopefully we
have convinced you that, okay I will give
it a shot.
So how do we really get started using the
C++ compiler in the project?
First, of course you need to make sure that
your tool chain is going to support C++.
Note that all compilers are not built the
same, some support some of the newer features
of the language while others don't.
As I mentioned we don't want to bite off too
much, so what we have provided here is an
initial set of steps to setting up your first
embedded C++ project.
This is generally considered the first month
learning and use of C++ in an embedded project.
At the very beginning and first and foremost
don't feel compelled to jump in and start
designing with classes.
This may shock some of you but we are going
to try and keep things simple to get started.
Start with something relatively small and
low risk such as standard procedural code,
especially if you are developing under tight
time constraints.
We urge you to not be overly ambitious getting
yourself into trouble with some of the more
complex and advanced features of the language.
Next, use C++ as a better C. Now what the
heck does that mean?
Well take advantage of some of the useful
features that are almost an extension of the
C language rather than a really different
programming paradigm such as using classes.
Once you have created a project or turned
on the C++ compiler for your tool chain, there
are a couple things we recommend turning off
immediately.
These two items are RTTI and Exceptions.
RTTI is Run- Time Type Information and this
is one of those areas of the C++ language
that is responsible for some of the code bloat
and performance issues.
The second item we recommend turning off is
exceptions and this is another area responsible
for code bloat and potential performance issues.
Unless you have a really good exception handling
strategy we would recommend turning this off.
So our first feature that we are going to
recommend you incorporate is namespaces, they
are free causing no code bloat or performance
issues and they can help you to define code
modules and entire libraries without fear
of name conflicts.
The next feature is default parameters, C++
has the feature where you can specify default
values for parameters and function calls.
And this is not just limited to member functions
of classes and structures but for all functions.
And then the next item we have got is references,
so C++ has this thing called a reference.
A reference is not a pointer to an object
but rather it is the object.
When we pass parameters as pointers there
is the need to check for null and the possibility
of corruption of a pointer or destruction
of a pointer.
A reference on the other hand doesn't need
to be checked for validity, we are guaranteed
the object exists.
A reference allows us to pass the actual object
without making a copy, so it has a performance
of a pointer while providing object instance
semantics.
We can pass parameters this way as well as
returning references to objects as needed.
Next is type casting, when we use C style
cast, there are times when we are just kind
of smashing, Square peg into a round a hole
this is potentially dangerous.
Because C++ is a lot more finicky about type
checking, the compiler will try to catch bad
cast for us.
There is no way of getting around the need
to do type casting.
As you move forward, consider always using
the C++ cast operators, while these will look
a little foreign and you may need to occasionally
look up, which one to use in which situation,
they are likely to ensure safe cast in your
program.
As a general rule try to use static cast,
this works for most data type casting.
Occasionally you will need to use reinterpret
cast.
It's a closest thing to a real C style cast.
Since we don't recommend using RTTI, dynamic
cast really won't be of concern to you and
we won't really talk about it here.
And then the last one is const cast, which
used with extreme caution, this is similar
to casting away the constance of an object,
so unless you really know that the object
you are casting away the constness from can
be done safely we recommend being very careful
in this area.
Slides 29-31: First Month Code Example
So here we see the header file that we are
using to illustrate a few things, you can
take advantage of right away as we discussed
in the last slide.
Namely, notice the use of namespace, we will
use the namespace and the source code to resolve
the name conflict at the global scope.
It also creates a nice little module or library
for us.
Notice also that we have declared the function
default_param_func1 to take a second optional
parameter with the default value of 255.
What that means is that if no value for the
second parameter is provided at a call site,
behind the scenes the compiler will insert
code to provide a value of 255 for the second
parameter.
Default parameters are useful not only for
making code more maintainable but they also
serve as a form of documentation.
Here we also have a method that takes a reference
and a couple of methods that will demonstrate
the use of a couple of the C++ casting mechanisms
that we have previously discussed.
So here we have our real source code and we
have a function name default_param_func1.
Notice that it actually does not use default
parameters.
The purpose here is to demonstrate a name
conflict.
The implementation is at the bottom of the
file.
In function main, we declare a couple of local
variables.
We then use the namespace syntax with the
scope resolution operator, the double colon
to differentiate which method we want to call.
On the first function call we don't pass the
second parameter.
This means we need a function that declares
it with the optional second parameter.
Namespace ensures the one we want is called
and we don't get a compiler error.
Next we explicitly call the same function
but it resolves at the global namespace followed
by the specific implementation in our namespace
again, this time including the second parameter.
In our experience there are at least two different
scenarios where namespaces really shine; the
first is in a large project with thousands
of functions in different areas.
With so many components and functions being
worked on by so many people often around the
globe, you are bound to have multiple functions
name init, reset, clear, things like that.
With namespaces you can have multiple functions
with the same name just make sure they all
reside in separate namespaces.
The second benefit of namespaces that you
can create a standalone library in its own
namespace, by placing all of the API in its
own well named, namespace and perhaps the
implementation details and another more hidden
namespace, you never have to worry about a
conflict in the future and you never have
to worry about who is going to use your code
today because it won't cause a conflict.
Okay, so back to the code, later in main we
printout that before and after values of the
local variables, before and after calling
function pass byref.
This call demonstrates passing my_var1 as
a reference allowing function pass byref to
change the value if it wants.
If you are a C only programmer, you probably
are thinking that these references seem a
lot like pointers.
We will take a look at that in a minute.
Getting back to the example, we assign my_var2
to the return value of this call before printing
the new values.
Last I would like to turn your attention to
a couple of cast in the code, the first is
in implicit conversion to avoid star that
works just like in C and then the second cast
is the reinterpret cast where we are casting
the pointer to my_var1 to a uint32_t, the
reason we use reinterpret cast here is, casting
between a pointer and its numeric representation
actually does require the use of reinterpret
cast.
Okay, so now here we have our source file
that implements the functions in our namespace
name first_month_lib.
Notice that the function names have the namespace
as a prefix.
First we have our name conflicted or not name
conflicted function that really does take
a second parameter with the default value.
Next we have a very simple function name pass
byref that accepts its single parameter by
reference indicated by the ampersand sign
in front of the parameter.
When we increment the value of the parameter
with param1, this is actually changing the
value of the object to clear back in main.
In this case, a simple data type incrementing
its value by 1 this means that calling function
will see this change in the object after the
call to pass byref is returned.
If instead we pass the parameter by pointer,
defensive coding requires that we check the
pointer is valid.
And what do we do if it isn't valid?
Do we assert, maybe, it's better to avoid
the problem in the first place if possible?
Here when passing by reference we know that
the object is initialized C++ guarantees unless
somebody did something really unsafe that
the reference is valid.
We can do similar things with complex data
structures as well.
And what if we don't want the value to change?
Okay, you can specify the parameter as a reference
to const, this implies from an API perspective
that the value should not be changed by the
called function.
I say, should because of course the called
function could cast away the const but if
you start casting away const you have been
warned and bad things most likely lie ahead.
The last couple of things to have a look at
here are the uses of the C++ cast, in the
first case, we have cast a void start to an
int32 pointer which can be dereferenced and
used.
In the second cast, we are performing essentially
the opposite cast that we perform back in
main going from a numeric representation of
the pointer to a pointer to an int32 object
which again can be used.
Slides 32-33: The First Year , My First Class
Okay we have gotten through our first month
of doing some basic C++, we have followed
some basic rules to keeping things simple,
we have done things like removing RTTI and
Exceptions, covered some new syntax, using
default parameters and references.
So what's the next logical step?
Well, let's discuss creating your first class
and let's also start here using some baby-steps.
Our best advice is to start simple again.
Don't plan on using any inheritance in your
first class, inheritance and eventually polymorphism
are topics you can investigate once you have
learned the fundamentals of class design and
we will get to that after we get past our
first class.
Essentially a class is like a standard C structs
in that it can be used to bundle data together.
For example, a vehicle class might define
a color or cost, weight and fuel economy.
Building on C structs, classes can also contain
member functions also called operations or
methods which allow operations to be performed
on objects of the class.
For the brief time we have in this webinar,
we can't fully cover the concept of access
specifiers for data members and methods.
These are also known as public, private and
protected but as you proceed in class design
you want and need to understand these a bit
more.
So we would recommend you investigate this
a bit more as you get further into class design.
Also just a brief mention, that in C++ classes
and structs are essentially the same except
that the default access specifier in the class
is private whereas in a struct it is public.
Okay so let's get back to class design, first
define the purpose of your class.
What function or functions is it intended
to perform.
And I mean this from a requirements point-of-view.
Think about what data it may manage operations,
you would like it to perform inputs and outputs
and the like.
Ask yourself, can the data and functionality
be encapsulated or does the data need to be
made readily available to the users?
The reason I ask this and something that is
a common mistake is wrapping a bunch of Plain
Old Data or POD into a class with a whole
mess of accessor methods or getters and setters.
A suggestion we have is that if it's Plain
Old Data just keep it Plain Old Data.
The point here is that you need to consider
the level of encapsulation of your data as
well as your operations you expose for utilizing
your class.
The public operations that the class provides
is essentially the class's interface.
Similar to C these are the methods you would
provide to your class for users for either
accessing data or performing operations.
Functions intended for internal use are not
part of the interface and shouldn't be exposed
for general use, especially if the method
depends on the internal class implementation
details.
This design philosophy is probably very similar
to things you may already be doing in C with
static functions versus globally accessible
methods.
An example of this will help explain these
concepts.
Slide 34: My First Class
Before I define my first class, I want to
differentiate a couple of terms.
An instance of a class is referred to as an
object.
So once a class has been defined on the stack
or statically allocated, it is now an instance
of the class, also known as an object of that
class type.
So for our first class let's create a simple
person class, for demonstration purposes a
person object can walk, run or stand still
and also for simplicity a person is just going
to have a gender and an age in years.
The person classes interface needs to match
these simple requirements.
As part of the interface we are also going
to provide a diagnostic function.
Slide 35: My First Class Header File
Okay here we have a header file that defines
a couple of enumerations and our person class.
Notice that the enums are actually types that
we later use in our person class.
In our person class notice that all data is
declared as private, this way no code outside
the object member functions can access or
change the data directly.
We have implemented a few simple accessors
also known as getters and setters.
Now we have also known as declared some operations
to change the state of the person such as
walk run and stand.
Take note that some of our interface is actually
implemented inline in the class, this is a
nice feature of C++.
For very simple operations it often makes
sense to provide the implementation right
in the class.
Some people may argue it should be implemented
as inline outside the class decoration but
this is really a personal preference.
Slide 36: My First Class Use
Using the person class in our test case is
fairly straightforward.
We are declaring a person object on the stack.
Note we are not dynamically allocating the
object from the he.
This is an important point to those of you
coming to the embedded world, especially if
coming from JAVA or C#.
Not every object needs to be created with
new or use dynamic allocation.
We are using the diagnostic method dump_info
to dump information about the object that's
our diagnostic function.
If you run this, you will see that at first
the state of the person object has all default
values.
As we invoke operations on the object such
as walk or stand or change the age of the
person the internal state of the object will
actually change.
Slides 37-38: My First Implementation
Here we see the implementation of the first
few functions of the person class, the first
function to discuss is the constructor of
the person class.
Notice that the name of the constructor for
the class is the same as the name of the class
itself.
Also notice there is no return type.
We will briefly discuss what that highlighted
colon and new syntax that follows for member
variables is in our next example.
We also have our various operations of walk
run and stand, these are simple operations
that you can perform on a person object as
we discuss as part of our interface.
What's really nice here is that we have encapsulated
as functionality into a single data type that
maintains our data and gives us access to
associated operations within the context of
the data.
We might have another run function that has
completely different purpose and is associated
with a complete different type of data structure,
all of this is possible in C it's just a bit
more work to get there.
Here we have got the remainder of our implementation
of the person class.
This is just our diagnostic method, instead
of using printfs we might decide we want to
tie into an existing logging mechanism or
send things out UART or something like that,
but what's nice here is it's been encapsulated
in the context of our person class so that
we can change it as we see fit.
Slide 39: More Class
So now we have some experience under our belts
with using a basic C++ class, it is simple
but we introduce some important concepts already,
constructors, access specifiers, data members,
member functions and encapsulation.
Now let's move on to something a little bit
more useful and sophisticated, let's look
at a simple class hierarchy to get you started
with virtual functions and abstractions.
Let's quickly review what inheritance gives
us in class design, common mistake is the
application of the Is a rule.
For example, a rectangle is a shape or a cat
is an animal.
Now there is sometimes or perhaps often works
it doesn't always, our advise is to ask that
is class A substitutable for class B when
deciding whether A can derive from B. Using
inheritance in C++ classes is one of the languages
most attractive features but use our strength
here especially when starting out otherwise
you may end up with a so called cosmic hierarchy
that no one but you understands.
In our experience this is one of the things
that gives C++ a bad name, where things just
become overly complex.
The other items we will show in our next example
is use of Const Member Functions or continued
use of Initializer Lists and Virtual Functions.
Some of the really nice features of class
design in the C++ language.
Slide 40: My Second Class(es)
We will start by defining our supporting data
types, once we have that in place, we will
define a base class with the intent of extending
or deriving from it.
This is referred to as an abstract class.
An abstract class is not meant to be instantiated
but more as a basis for creating more concrete
data types.
However as we will see we can still use the
abstract class to treat objects polymorphically.
What we are going to do is create a drawing
library, we will start with a base class that's
a shape class, the concept of the shape is
abstract.
For our purposes we are going to keep it really
simple, we all know what specific shapes we
can have a line, circle, rectangle, triangle
things like that.
Our shape class will be simple, it will have
a color data member and a draw function, just
for now.
Note that other attributes like area, number
of sides etc. might seem like a good idea
but they fall apart in the face of shapes
like circle or line.
Our draw method is pure virtual indicated
by the equals zero in the function's declaration
and we will see this in the example, this
is because there is no single implementation
that draw all shapes.
This is always a good hint to what needs to
be implemented in our derived classes.
Slide 41: Support and Base Class(es)
In this slide we see a couple of data structures,
let's first look at the top one the point_t
struct.
Notice, its struct with member functions as
it turns out and as mentioned a struct and
a class are almost identical, the only difference
is the default access level is public in struct
and private in class and that's it.
What we have done here is create a point structure
and look we have got two constructors in a
struct.
Now we can encapsulate initialization mechanisms
right in our structure to ensure we have something
valid instead of uninitialized data.
We have provided a default construct here
as well as one that initializes our data members.
A point structure is Plain Old Data so there
are no accessors in this example.
Notice similar to earlier the colon followed
by the comma delimited member variables that
have values in parenthesis this is called
an initializer list.
It allows us to initialize our objects data
before entering the body of the constructor,
it is considered the best practice to initialize
data in this manner and mainly for efficiency
reasons, however not all types of member data
can be initialized in an initializer list.
Now for our shape class this is an abstract
class, I will explain how we ensure that in
just a minute.
First let's look at the class definition,
we have a data member of type color_t we have
made it protected so that derived classes
can access it directly, but other outside
classes and code can't.
Next we have our default constructor, this
is a default constructor because my single
parameter to the constructor is optional,
specifying a default color of black.
Note again that we initialized our color data
member with a pass parameter.
There is a bit of a side bar color_t is an
enumeration of color values not shown here
and the valid values are blue, black, green
and red.
Similar to the earlier example, enumeration
is a type and used as such in the shape class.
Now we get to the real reason this class is
abstract, we have our single draw method that's
declared as virtual and assigned to zero.
This is referred to as a pure virtual function.
The compiler will raise an error if you try
to instantiate a shape_t class because the
draw method is pure virtual.
Also notice that const in the declaration
of the draw member function, this is something
you wouldn't ever see in C. The const indicates
that the member function won't change, modify
or mutate the object's member data.
So now let's take a look at a couple of classes
it derived from shape_t that can actually
be instantiated and used.
Slide 42: Support and Base Class(es)
Our first derived classes is a line_t notice
the colon followed by public and then shape_t,
this means it derives from shape_t and that
the base class is publicly accessible to users
of any instance of a line_t object.
You might be asking, what does that mean?
It means that any methods or data made available
in shape_t as the base class can be directly
accessed from a line_t object.
Okay, so line_t derives from shape_t we are
declaring some private data to element array
of type point_t that we saw on the last screen.
These will be the end points of the line as
they are declared as private, no other classes
can access them, not even a derived class.
Notice that we pass the lines color to the
base class constructor.
Now in the body of the constructor, we initialize
our two end points.
Arrays can't be initialized in an initializer
list so it has to be done in the body of the
constructor.
Next we have line_t's implementation of the
draw function.
If we don't implement it here, we won't be
able to instantiate a line_t object.
The implementation is inline for brevity.
Now let's create another shape that might
be useful, so how about a circle.
Slide 43: Our Second Class(es)
Okay so here's our next shape class a circle
as mentioned, that will give us another drawing
primitive class.
Again we will derive from shape_t just like
we did with line_t.
We define our constructor with the required
data, again calling the shape constructor
with the color value as a parameter.
We can initialize m_center and m_radius in
the initializer list since it do have constructors
that we can call.
And like line_t we declare and implement the
draw function and we are done.
We now have two useful shapes that can be
used to draw in a very generic way.
And we will show that in the next slide when
we actually use our shape classes.
As I am sure you can imagine, you can create
a number of other classes for your drawing
primitives like rectangles, polygons, filled
objects and you could also extend these classes
in many ways.
Slide 44: Using Our Second Class(es)
What we have for example implementation is
very simple, but demonstrates our classes,
especially the use of polymorphism.
We have a method named do_something_interesting.
It takes a reference to a shape object.
If you have been following you would say,
but wait, you said we can't create a shape
object and you would be right.
However, we are not creating a shape object
here, we are actually going to create objects
derive from abstract shape class and then
pass them to our do_something_interesting
method.
In main we see an instance of a line and a
circle object.
Each created by passing the necessary parameters
to properly construct each object.
Notice similar to our earlier example that
these objects are not created using dynamic
memory, they are both on the stack.
Next, we see that we are calling the do_something_interesting
method with each object.
Aha the compiler has recognized that each
of these objects is in fact a shape object
and implicitly up cast each type to a shape_t.
We are passing as a const reference.
This ensures that we can't change the object
and being a reference we are guarantee that
the object is valid.
So there is no need to check for null pointers.
And there is no chance that our pointers to
these objects would get corrupted.
And to do something interesting function,
we call the draw function which calls the
draw member function for each specific class,
remember draw is declared as virtual, this
will call the specific implementation for
line and circle objects.
This is a very powerful mechanism known as
polymorphism, well this can actually be achieved
in C, it's much easier to do in C++ and there
is a lot less effort on the part of the programmer.
Perhaps you are already thinking of examples
in your own product that can benefit from
abstraction and polymorphism like this.
And that brings us to the end of our examples
and what we are going to cover in our webinar
today.
Slides 45-50: Recap
Before we leave you let's recap the key points
we have made here.
We have covered a lot of ground and hopefully,
you have learned a thing or two and that we
have piqued your interest.
C++ provides a lot of useful features to help
us build safe, reliable, embedded systems
hopefully you have learned something you can
use in your next project or perhaps even the
current one.
We recommend trying out some of these concepts
following the strategy we have provided starting
simple and small.
Start out by preparing your existing code
for use in a mixed language environment.
So you can take your time migrating and adopting
C++.
Try to go even further than what we have done
here, there are a lot of great features in
the C++ language that we didn't have time
to cover today.
We strongly recommend finding a good set of
resources whether human, books, websites or
otherwise and even look to each other for
support and learning opportunities.
Lastly consider strength in your object oriented
design and programming skills, these concepts
apply to C as well as C++.
C++ just makes it a little bit easier to implement
an object oriented design.
And at this time, I would like to open things
up for any questions you might have.
