In early January, I wrote a post urging that C++ be considered more seriously for microcontroller development. A lot of people weighed in with their thoughts about the best language for embedded use. Many of the comments showed the fears that many C developers had of C++, so I decided to test the assertion that C++ is C's bloated, riskier child.
I decided to perform a simple n=1 experiment. In this experiment, a program was written using C++ and then ported to C. The object of this program was to print a string in Morse code to the RGB LED on the KL25Z Freedom board. Below is a video of the program signaling "Micro Controller Central."
This program is by no means stress loading the processor, but it does use two of the chip's peripherals (GPIO and Timers) and two additional higher-level modules. The overall hierarchy of the program looked like this.
A lot of people who commented on the January post expressed concerns about C++ distancing the developer from hardware, but these two programs look nearly identical. The main difference is that, in C, you cannot instantiate three instances of a library, so one module is required to control all three GPIOs. The full source code can be found on my GitHub.
And now for the results. Drum roll, please.
Table 1: Code size as compiled using Keil 4.6, optimization level 0
Although this data is by no means conclusive, it does show that a sample program written in C++ that uses only basic features of the language is only negligibly more bloated (and is actually less bloated in RW data) than the same code written in C. Now, you may be asking yourself, "If these are so close, then why bother learning a brand new language that produces such similar code?"
In my previous post, I highlighted one of the major upgrades that C++ could offer C users: the access control given by classes. To demonstrate this functionality, I included a fake function in the MorseCodeMessenger module in both C (link to file) and C++ (link to file) called "removeCriticalSafety." This hypothetical piece of code removes a fail-safe from the system, allowing it to potentially become unsafe.
Let's say that, during prototyping, two engineers were working on developing a system using the MorseCodeMessenger module -- one using C and one using C++. During the early stages of prototyping, the removeCriticalSafety code had to be tested in the main application code, so this code was made publically available in the header files. After this function was proven, the engineer working with the C program moved the function definition to the top of MorseCodeMessenger.c, while the engineer working with the C++ program moved the function definition to the private block of the MorseCodeMessenger class. Both engineers, being very tired on a Monday, forgot to remove the function call from the main application block.
When the engineer working in C hits the compile button, it will successfully compile with the following warning:
Source\main.c(45): warning: #223-D: function “morseRemoveCriticalSafety” declared implicitly
The program will run successfully and still call the removeCriticalSafety function. This is disturbing, since the engineer may miss this message (or never even see it if compiler warnings are suppressed). In this situation, the engineer working in C will now be developing and running software on a potentially unsafe system.
On the other side of the office, when the engineer writing the program in C++ hits the compile button, the following error will come out:
Source\main.cpp(51): error: #265-D: function “MorseCodeMessenger::removeCriticalSafety” (declared at line 35 of “Source\MorseCodeMessenger.h”) is inaccessible
The target will not be created, and the engineer working in C++ will be forced to correct this error before proceeding with development.
What lessons can be taken from this experiment? The most obvious is that you should never code on a Monday morning -- bad things will happen. Beyond this, this experiment reinforces my belief that properly written C++ will not force you into buying a more powerful chip. As long as you don't use all the bells and whistles of C++, switching will have only a minor impact on performance while improving your program's safety and readability. This is not to say that the more advanced features of C++ are worthless in embedded systems -- if the requirements of an application expand, these advanced features give plenty of headroom to grow.
I plan on adding more functionality to these programs to stress the processor and increase the code size, but the results look very promising for now. Why not try writing one yourself to make this an n>1 experiment?
Nice summary of the differences and 'use models' of C and C++. C is very powerful, but when used incorrectly can really get you in trouble. C++ tries to retain much of the flexibility of C but protects the user from some common C 'errors'.
The comparision of compiled code-size shown in this blog for C and C++ does depend on the C++ (and C) compilers used. It is good to know a C++ compiler can produce compiled code of almost the same size as a C compiler.
C++ as such, is a superset of C. Historically, one of the reasons C++ was invented was to help manage the complexities of large Projects. C++ came to mainstream programming as software Projects grew larger and more complex. But, as some of you pointed out, it is possibe to write bad code in both, C and C++.
And when coding in C, do not ignore "all" the warning messages even on a Monday morning.
northstar 2/9/2013 11:51:49 AM User Rank Program Manager
Re: I predate C++
If you know a little bit of Python or Java, C++ is really simple to understand. The big thing is to get used with object oriented programming, like everything is an object. Objects have methods(functions) to interact with the eternal world and internal variables(members) that hold values. Basically, that it. Define an object being a class. Class functions are functions used to access object functionality. Functions are modifying internal variables(members).
Now, think that C structures(struct) are objects(classes) in C++. This is an equivalance. They are the same. As you have struct members, you also have object members. It is the same thing. One more thing for C++ objects vs structs in C are functions(methods) that are used to interact with class(objects) members.
If thisi is understood, then you can use C++ with no fear. The object programming thing is actually help you to organize your program, from a logical perspective. Think that objects are close entities that have values(in internal members) and can interact with the world through a well defined interface: methods(functions) of the object.
Now it is easy to think that you can have an object that know how to work with SPI. Another one that know how to work with GPIO. Another knows hoe to work with leds. Now, in main programm, just create 3 objects of those types and using their functions, use their functionality. The program logic becomes more clear, becuase each object functionality is enclosed into that object.
northstar 2/9/2013 11:41:49 AM User Rank Program Manager
Re: I predate C++
Max, if you learn Python, you can safely ignore C++. If you get used with object oriented programming, it will be easy to go with C++. There are just few more things to add(like polymorphism and interfaces), but if you know Python you will learn those fast.
After all, C++ is a friendly programming language. Of course, so things (like templates) can easily scary anyone, but you really do not need those.
As do I. When I was learning programming, the topic of the day was the transition from linear programming to structured programming. Those were the days of the "goto wars." Not everyone could make that transition.
Next, event driven programming threw another monkey wrench in, but was necessary for GUI programs. Somewhere in that time span, there was client/server programming. That and thin-client came and went a dozen times before setting in.
I got stuck in the transition from all of that to object oriented programming. The whole thought process is different. I've been gaining little bits of understanding over time, but wasn't able to just jump to C++. The mbed is nice in this regard because of the way it easily supports both C++ and C. I can program in the familiar C and add bits of C++ in as I start to understand.
When I was at university I learned Fortran, BASIC, and an assembly language. In my first job outside of university I learned Pascal -- and later C. Now I'm working on learning Python in my spare time. I'm not a programmer -- I'm a hardware developer who meandered into being a writer. Unfortunately the whole C++ thing passed me by -- I'd love to learn more about C## and C# ... but there's so much to do and so little time to do it all in (sad face)
embd_dragon 2/5/2013 6:41:59 PM User Rank System supervisor
Re: C vs C++
"Abstraction does move some of the housekeeping a layer farther away from the programmer and can lead to the programmer being less aware of the hardware, which with microcontrollers is extremely important. "
The more abstraction level, the better. It depends on what you want. If you are the driver's developer, of course you don't need any abstraction level at all; however, the driver's client, or the module's client both will wish a higher abstraction level.
I don't like either Arduino nor MBED; nonetheless, their HALs are awesome. If you want to write an application ASAP, so make use of the higher abstraction levels. In the other side, if you are like many of us then enjoy the microcontrollers lowest posible abstraction levels.
"If you aren't paying attention to just how much memory an object takes for each instance you will run into trouble. "
Any object will need the same ammount of memory as their C counterparts. Why? An object is a collection of attributes (variables) along behaviors (methods).
There is no difference between two functions foo_int(int) and foo_char(char), 'cause in OOP you would have foo(int) and foo(char) (method overloading).
As I said before OOP is in the mind and not in the platform.
All of us should be using assembler instead C? I don't think so, what do you think? =)
What about this: low level drivers in C, module client's interface in C++ ;)
Unfortunately I got into programming and C before there were objects. We worked with structures to group related data, but not objects. And every time I have tried to figure out the benefit of objects, the examples have all come out more complex and harder to make sense of. And perhaps the examples were a bit trivial which made them make less sense. (Oh - and I did learn "Structured Programming" so I am not a total savage...)
The biggest problem that I have seen with C++ is that it is a younger set of programmers that have never had to work with memory constraints prior to using microcontrollers. They have never really had to worry about memory usage and they use all the features of C++ and because they are not trying to be super efficient they runinto problems. Abstraction does move some of the housekeeping a layer farther away from the programmer and can lead to the programmer being less aware of the hardware, which with microcontrollers is extremely important. If you aren't paying attention to just how much memory an object takes for each instance you will run into trouble. But then, some of that might just be the type of training they recieved earlier in their carreer.
A bigger issue that I have worked around with assembler - how much code do the libraries add just to protect registers that winds up not needed. If I use 3 registers in my function, why does the code always have 16 registers pushed and popped? Identical functionality C - 1200+ bytres, Assembly -88 bytes. Though it was nice to test out my idea in C and then write it in assembler.
Davidmicro 2/3/2013 4:40:39 PM User Rank Program Manager
Re: Lets Try c++
All I am saying is to get system constraints for 64 bytes of RAM and 1 KB of flash memory, not rule so that code developer must optimize code by complier option as well as code itself. I wish that there is a clarification for memory constraints as one of example.