C++ compilers are available for most modern embedded processors, yet the adoption rate remains low. Here we provide very practical advice to help you can get started with C++ immediately. The discussion moves quickly from dispelling common C++ myths and identifying key C++ benefits to a set of practical tips and tricks to help you put C++ to the most effective use in your "first month" and "first year."
Slide 1: Getting Started with C++ in Embedded Systems
Moderator:: Welcome and thank you for attending Barr Group’s webinar Getting Started with C++ in Embedded Systems. My name is Sherry, and I will be your moderator for the next hour. Today’s webinar presenters are Michael Barr (Chief Technical Officer) and Michael Wilk (Principal Engineer).
Slide 2: About Barr Group
Hello everyone and thank you for joining us. My name is Michael Barr, I am a co-founder and the Chief Technical Officer of Barr Group.
At Barr Group our mission is to help as many people as possible build safer, more reliable, and more secure embedded systems. We are an independent consulting firm, specializing in embedded systems and software. And we regularly consult with numerous companies in many industries on software process improvement and embedded systems (re-)architecture for systems and software. As well we take on design and development work, where we participate in the software and sometimes also electrical and mechanical design of systems. As well, we train engineers in best practices through webinars like this, through public training events and also at private companies. And finally, we sometimes, our expert sometimes testify before judges and juries about issues relating to embedded systems and embedded software.
Slide 3: Barr Group Training Courses
Before we begin today’s webinar, I would just like to give you three URLs relating to our training courses. The first is for our Training Calendar for upcoming public courses, this is where one person or a couple of people from a company can join others from the industry at a hotel meeting room or our headquarters training room to learn about different topics. And if you go to that first URL, you will find a list of upcoming courses, dates and locations.
The second URL I would like to bring to your attention is for our Course Catalog, this is a list of all of the courses that we offer, including courses that are offered publicly but also courses that are only offered at on-site locations at private companies. And of course any of the courses in the catalog can be brought to your company or it can be even sometimes be customized for your needs.
Finally, we have an archive of all of our Past Webinars. After today’s webinar has been transcribed and recorded--you can find this one there as well. And we have a number of other interesting and valuable webinars there. So with that I turn the microphone over to today’s speaker.
Slide 4: Michael Wilk, Principal Engineer
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 byte 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.
Question & Answer
Q: I am moving from doing almost entirely hardware design to doing almost exclusively firmware and embedded development. Should I go straight to C++ or learn C first?
This is a good question and actually fairly common, let’s see how do you answer that, okay. So I would say for a lot of hardware engineers that we have experienced there is a tendency to be kind of a little more low level or do a lot more simple application development, not that’s definitely not always the case, but the first thing I would probably say here is as a general rule, is to just learn good software development practices. You know develop code in modular and attestable manner and look for code reuse opportunities. Now there are times when using code reuse actually doesn’t necessarily align with performance needs, but as a general rule if you want to have nice modular and reusable code, you know try to adopt that as you move. I don’t think there is necessarily a right answer of whether to use C or C++ first but this may actually partly depend on the platform that you are going to. So for example you are going to an 8 bit pic, I don’t believe there is a C++ compiler for an 8 bit pic. So it probably doesn’t make too much sense to go straight to doing C++. But you can still adopt a lot of good software development practices and make things modular.
One of the things that I noted down here was don’t ignore a compiler’s warnings and don’t try to trick the compiler into doing things that you may think are correct. If the compiler is giving you a warning really consider what that warning is and make sure what you are doing is safe.
Q: I am moving from doing desktop development to doing embedded development. What are some of the difference to consider when using C++ on an embedded system?
So you are like, me. I, if you remember, from early on I mentioned that that’s kind of the world I came from where lots of CPU resources, lots of memory, no limitations on code space and things like that. So we need to take a lot of those things into consideration and I made note of a few things to consider when doing this move. First and we talked about this quite a bit was either avoid or really careful consider your use of dynamic memory allocation using the Heap. This is you know really common in most desktop applications or server development where resources are plentiful. Remember that your new target maybe an order of magnitude or more slower and have significantly less code space and RAM available.
Next, also very common in the desktop and server world is third party libraries. Be careful, using third party libraries, it could be very complex and you know could lead to performance issues and could be doing things under the hood that you aren’t aware of such as a lot of dynamic memory allocation and things like that. And it could get you into some trouble from either a resource point of view or a performance point of view.
And then lastly, when you are designing classes, this kind of goes back to some of our code blue issues, is don’t make your methods virtual as a just in case. Really consider when you are doing class design, if you are designing a class and you have some methods in there, don’t just make them virtual just because, this will create a vtable that may not even get used and or be necessary so consider that as well.
Q: Can I map hardware registers using C++ structures like I do in C?
Absolutely, there is pretty much no difference here, just still need to pay attention to proper use of your data structure and your data members. Make sure, that you have proper use of our volatile keyword, always so important. And you know what else do I have written down here as far as this goes, there can actually be an advantage here of moving to C++ to create a hardware abstraction layer. You can encapsulate a lot of the lower level access to your hardware in a C++ wrapper. Now you could do this in C as well of course but you can also adopt a lot of test driven development techniques and things like that where you could create hardware simulations and things like that through your class design. You can also incorporate if necessary critical sections or mutex or whatever it maybe protecting access to your hardware peripherals in ensuring that that’s all done properly as you access your hardware.
Q: Can I use C++ with an RTOS such as FreeRTOS or MicroC/OS.
Yes, absolutely we have done this, quite a bit. One of the things we have done and seen here is implementing a set of well either, well okay there are something called a Operating System Abstraction Layer an OSAL and what you can do here is create an abstraction layer so that accessing the RTOS is performed through a set of wrappers or in an abstract way. So then even though people will say oh, the operating system is never going to change and well today, you know we are even FreeRTOS tomorrow we are using VxWorks. If you have abstract away the access to the operating system, this makes things a lot more portable. We have seen frameworks that have imported for simulation purposes. And some of the things you can take advantage here of is really creating modules and thread, you know your thread classes or your thread objects in a very abstract way you can put a lot of that low-level boiler plate code, say your why loops and things like that, in the design of a thread type class and then you can use inheritance to implement and extend that class to implement your specific functionality, for a given thread type object. We actually do this quite a bit. You can incorporate things like the messaging or communication mechanisms on how threads communicate with one another and you create a real separation on concerns, in your thread derived classes.
Q: Within a rough estimate, how often is C++ use for embedded design in today’s world?
I don’t know if I have a direct answer for that but according to the Barr Group survey that was done a few months ago, we had approximately 20% of the respondents say that they were actively using a C++ in their current embedded projects. So if that is accurate which I think it’s pretty accurate, I don’t see any reason for anybody to lie about that. There is, you know that’s about a space, at least for the survey respondents that have said that they are actively using it in their embedded designs.
Q: You mentioned having access to resources, what are some good or specific resources for C++?
That’s a good question. I have actually there is a number of older and existing articles on embedded.com, number of articles written by Dan Saks, I believe Coleen Wallace has written a number of articles, there is a lot of good information out there. One of my favorite resources for C++ is Marshall Cline’s C++ FAQ, F-A-Q, a lot of great information there.
There is of course a lot of C++ books out there but there is not a lot of real specific doing C++ in for embedded specifically. So that’s a little more limited but there is a lot of good groups out there on say LinkedIn and other places that you can join and see what people are talking about. One of the things to remember here though is, if good C++ is good C++ whether it’s embedded or not. So if you come across the book that talks about whether it’s design patterns or class design or object orient design in C++, things like that. You are going to be able to adopt those good C++ practices, but be sure to keep in mind some of the things that we have talked about here, as far as you know code bloat and performance issues and things like that and you know still start simple and build up things. One of the things that we really like to encourage is, is leaning on each other, talk to each other, work through problems, sit down and maybe discuss various topics together maybe at lunch time or something like that. I have actually found that that’s pretty effective.
Q: I am on a team that have been trying to convince we should use C++ for our next big project but nobody has any real experience and my experience is limited. Is it too risky to adopt C++?
Well, I would probably say here if you look back on this webinar and you look at some of the strategies that we have put forth moving in smaller steps, starting simple, also talking about leaning on each other, don’t try and adopt really complex concepts of either OO or C++, start simple, build up from there, again lean on like each other, look for somebody who can hold your hand, a little bit that has gone through it. One of the things that I like to say is you know how experienced are you and by experience what I usually think of when it’s experience, it means, well, you made a decision, it was the wrong decision and you learn from your mistakes and you know how to do it the right way next time. Really listen to those “experienced people” that have gone through it and have learned things the hard way or learned from other people that have learned from doing things either the wrong way or for things that really work. And you know look for those resources as mentioned in that previous question.
Q: What is "Embedded C++"?
Okay, embedded C++ is a subset of the C++ language, a lot of the useful features of the language, I would say, have actually been removed. So I would say it’s I may get some criticism for this but I would actually say it’s not necessarily the most beneficial approach, because you have lost some of the key or extremely useful features of the C++ language. I believe that even namespaces have been banned from embedded C++. And I also believe that C++ style casting has been eliminated from embedded C++ I guess we will call it a language. So that’s really unfortunate, so anyway I think hopefully that answers, so I think we are out of time here, and I am going to turn it back over.
Related Articles
How to Code a State Machine in C or C++
How to Create Jump Tables via Function Pointer Arrays in C and C++
CRC Series, Part 3: CRC Implementation Code in C/C++