I have two compilers on my hardware C++ and C89
I'm thinking about using C++ with classes but without polymorphism (to avoid vtables). The main reasons I’d like to use C++ are:
Do you see any reason to stick with C89 when developing for very limited hardware (4kb of RAM)?
Thank you for your answers, they were really helpful!
I thought the subject through and I will stick with C mainly because:
It is hard to accept one answer as you provided so many good answers. Unfortunately I can't create a wiki and accept it, so I will choose one answer that made me think most.
C provides optimized machine instructions for the given input, which increases the performance of the embedded system. Most of the high-level languages rely on libraries, hence they require more memory which is a major challenge in embedded systems.
C is generally used for desktop computers, while embedded C is for microcontroller based applications. C can use the resources of a desktop PC like memory, OS, etc. While, embedded C has to use with the limited resources, such as RAM, ROM, I/Os on an embedded processor.
Because C is the fundamental in almost every programming language, and the start for any processors. Also, C++ standard library is very complicated to port to other embedded systems, so it is very hard task for the compiler programmer.
C is a much smaller language to write a compiler for, so a lot of small CPUs have a C compiler available. C++ is massively more difficult, so doesn't often happen. As a result, you can rely on C code for any given chip, but less so having C++ available. This trains embedded coders to use C as their main language.
For a very resource constrained target such as 4KB of RAM, I'd test the waters with some samples before committing a lot of effort that can't be easily ported back into a pure ANSI C implementation.
The Embedded C++ working group did propose a standard subset of the language and a standard subset of the standard library to go with it. I lost track of that effort when the C User's Journal died, unfortunately. It looks like there is an article at Wikipedia, and that the committee still exists.
In an embedded environment, you really have to be careful about memory allocation. To enforce that care, you may need to define the global operator new()
and its friends to something that can't be even linked so that you know it isn't used. Placement new
on the other hand is likely to be your friend, when used judiciously along with a stable, thread-safe, and latency guaranteed allocation scheme.
Inlined functions won't cause much problem, unless they are big enough that they should have been true functions in the first place. Of course the macros their replacing had that same issue.
Templates, too, may not cause a problem unless their instantiation runs amok. For any template you do use, audit your generated code (the link map may have sufficient clues) to make certain that only the instantiations you intended to use happened.
One other issue that may arise is compatibility with your debugger. It isn't unusual for an otherwise usable hardware debugger to have very limited support for interaction with the original source code. If you effectively must debug in assembly, then the interesting name mangling of C++ can add extra confusion to the task.
RTTI, dynamic casts, multiple inheritance, heavy polymorphism, and exceptions all come with some amount of runtime cost for their use. A few of those features level that cost over the whole program if they are used, others just increase the weight of classes that need them. Know the difference, and choose advanced features wisely with full knowledge of at least a cursory cost/benefit analysis.
In an small embedded environment you will either be linking directly to a real time kernel or running directly on the hardware. Either way, you will need to make certain that your runtime startup code handles C++ specific startup chores correctly. This might be as simple as making sure to use the right linker options, but since it is common to have direct control over the source to the power on reset entry point, you might need to audit that to make certain that it does everything. For example, on a ColdFire platform I worked on, the dev tools shipped with a CRT0.S module that had the C++ initializers present but comment out. If I had used it straight from the box, I would have been mystified by global objects whose constructors had never run at all.
Also, in an embedded environment, it is often necessary to initialize hardware devices before they can be used, and if there is no OS and no boot loader, then it is your code that does that. You will need to remember that constructors for global objects are run before main()
is called so you will need to modify your local CRT0.S (or its equivalent) to get that hardware initialization done before the global constructors themselves are called. Obviously, the top of main()
is way too late.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With