You are on page 1of 9

C++

C++ in embedded systems:


Myth and reality
By Dominic Herity superior to C for embedded sys- Listing 1 Listing 2
Senior Software Engineer tems programming.
Function name Function name
Silicon and Software Systems It aims to provide detailed
overloading. overloading in C.
understanding of what C++
// C++ function name /* C substitute for */
I was surprised a while ago by code does at the machine level, overload example /* function name over-
what I heard in a coffee break so that readers can evaluate for void foo(int i) load */
conversation during a C++ themselves the speed and size { void foo_int(int i)
course. The attendees were spec- of C++ code as naturally as they //... {
} /*... */
ulating on the relevance of C++ do for C code. void foo(char* s) }
to embedded systems, and the To examine the nuts and { void foo_charstar(char*
misconceptions that surfaced bolts of C++ code generation, //... s)
drove me ultimately to write this I will discuss the major features } {
void main() /*... */
article. of the language and how they { }
Some perceive that C++ has are implemented in practice. foo(1); void main()
overheads and costs that render Implementations will be illus- foo(“Hello world”); {
it somehow unsuited to embed- trated by showing pieces of C++ } foo_int(1);
foo_charstar(“Hello
ded systems programming, that code followed by the equivalent
world”);
it lacks the control and brevity of (or near equivalent) C code. a C compiler and the machine }
C, or that, while it may be suited I will then discuss some pitfalls code generated will be exactly
to some niche applications, it specific to embedded systems what you would expect from a C Listing 3
will never displace C as the lan- and how to avoid them. I won’t compiler. A trivial class with
guage of choice for embedded discuss the uses and subtleties This simple point invalidates member function.
systems. of C++ or object-oriented (OO) any claims that a system can be / A trivial class
These perceptions are wrong. design, as these topics have implemented in C, but not in class foo
Where compilers and other tools been well covered elsewhere. C++. In practice, existing C code {
are adequate, C++ is always can typically be re-compiled private:
int x;
preferable to C as an implemen- The Myths as C++ with about the same public:
tation language for embedded Some of the perceptions that dis- amount of difficulty that adopt- void bar();
systems. While doing everything courage the use of C++ in embed- ing a new C compiler entails. };
that C does, it offers greater op- ded systems are: This also means that migrat- void foo::bar()
{
portunities for expression, en- • C++ is slow ing to C++ can be done gradu- x = 0;
capsulation, re-use, and it even • C++ produces large binaries ally, starting with C and working }
allows size and speed improve- • Abstraction leads to in new language features at your
ments that are impractical in C. inefficiency own pace. Although this isn’t the Listing 4
Why, then, do these percep- • Objects are large best way to reap the benefits of
tions persist? The main reason • Virtual functions are slow OO design, it minimises short C substitute for trivial class
with member function.
is that when people form their • C++ isn't ROMable term risk and it provides a basis
/* C substitute for
opinions, they know a lot more • Class libraries make large for iterative changes to a work-
trivial class foo */
about C than about C++. They binaries ing system. struct foo
have read some books, written {
some code, and are competent Some of these perceptions are Front end features: a free int x;
};
at using the features of C++, exaggerated concerns. Others are lunch
void bar_foo(struct foo*
but they lack the knowledge of just wrong. When the details of Many features of C++ are strictly this)
what is happening under the C++ code generation are exam- front end issues and have no ef- {
hood, the familiarity that allows ined in detail, it will be clear what fect on code generation. The ben- this->x = 0;
}
one to visualise the disassembly the reality behind these myths is. efits conferred by these features
while typing source or even are therefore free of cost at run
while formulating a design. It is Anything C can do, C++ can time. These features include use between const and non-const
to these people that this article do better of the keywords const, private, data. These specifiers allow the
is addressed. The most obvious property of protected, and public, which al- programmer to prevent misuse
This article aims to replace ex- C++ is so obvious that it’s often low the programmer to prevent of data or interfaces through
aggerated claims and misapplied overlooked: C++ is a superset of misuse of interfaces. No physical compiler-enforced restrictions.
generalisations with informed C. If you write a code fragment difference exists between private, Default arguments to func-
comment. It supports the view (or an entire source file) in the C protected, and public members. tions are another neat, free front
that C++, used appropriately, is subset, the compiler will act like Neither is there a difference end feature. The compiler inserts

 eetindia.com | February 1998 | EE Times-India


default arguments to a function generates a reference to a label Function names are altered to Behind the protection and
call, where none are specified by such as ?foo@@YAHH@Z, while add argument types, so that the scoping, a class is almost the
the source. a call to a function void foo(int) two functions have different same as a C struct. Indeed, in
A less obvious front end fea- generates a label like ?foo@@ names. C++, a struct is defined to be a
ture is function name overload- YAXH@Z and a call to a function In C++, name mangling is au- class whose members are public
ing. Function name overloading void foo(bar*) generates a label tomatic, but in a C substitute, it by default. A member function is
is made possible by a remarkably like ?foo@@YAXPAU bar@@@ would be the responsibility of the a function that takes a pointer to
simple compile-time mecha- Z. Name mangling ensures that programmer. an object of its class as an implicit
nism. The mechanism is com- functions aren’t called with the parameter. So a C++ class with a
monly called name mangling, wrong argument types and it Classes, Member Functions, member function is equivalent,
but has been called name deco- also allows the same name to and Objects in terms of code generation, to a
ration by those who have noth- be used for different functions Classes and member functions are C struct and a function that takes
ing better to do than sanitise provided their argument types the most important new concept that struct as an argument.
perfectly good terms. Anyone are different. in C++. Unfortunately, they are Listing 3 shows a trivial class
who has seen a linker protesting Listing 1 shows a C++ code usually introduced without ex- foo with one member variable x
the absence of ?foo@@YAHH@ fragment with function name planation of how they are imple- and one member function bar().
Z knows which term is more overloading. There are two func- mented, which tends to disorient Listing 4 shows the C substi-
appropriate. Name mangling tions called foo, one taking an int C programmers from the start. In tute for Listing 3. Struct foo has
modifies the label generated for argument, the other taking a char* the subsequent struggle to come the same member variable as
a function using the types of the argument. to terms with OO design, hope of class foo and the member func-
function arguments, or function Listing 2 shows how this understanding code generation tion foo::bar() is replaced with
signature. So a call to int foo(int) would be implemented in C. quickly recedes. a function bar_foo(struct foo*).

Listing 5 Listing 6
A simple string class featuring constructors, C substitute for simple string class.
destructors, operator overloading, new, and delete. /* C substitute
// A simplified string class for simplified string class */
#include #include
#include #include
using namespace std; #include
class String { struct String {
private: char* data;
char* data; unsigned len;
unsigned len; };
public: #define length_String(s) ((s)->len)
String(); void StringConstructor(String* this) {
~String(); this->len = 0;
unsigned length() const; this->data = 0;
String& operator=(const char* s); }
}; void StringDestructor(String* this) {
inline unsigned String::length() const if (this->data != 0)
{ free(this->data);
return len; }
}; String operatorEquals_String_const_char_star(
String::String() { String* this, const char* s) {
len = 0; this->len = strlen(s);
data = 0; this->data = (char*) malloc(
} (this->len+1) * sizeof(char));
String::~String() { /*
if (data != 0) If char had a constructor, */
delete [] data; /* it would be have to be */
} /* called here. */
String& String::operator=(const char* s) { if (this->data == 0)
len = strlen(s); this->len = 0;
data = new char [ len+1 ]; else
if (data == 0) strcpy(this->data, s);
len = 0; return *this;
else }
strcpy(data, s); FILE*
return *this; operatorShiftLeft_ostream_unsigned(FILE*,
} unsigned);
void main() { void main() {
String s; String s;
s = “Hello world”; StringConstructor(&s);
cout operatorEquals_String_const_char_star(
< &s, “Hello world”);
< operatorShiftLeft_ostream_unsigned(stdout,
s.length(); length_String(&s));
} StringDestructor(&s);
}

 eetindia.com | February 1998 | EE Times-India


Note the name of the argument
Listing 7 Listing 8
of bar_foo(struct foo*) has been
chosen as this, which is a key- Inheritance. C substitute for inheritance.
word in C++, but not in C. The // Simple example of in- /* C Substitute for inheritance */
heritance struct A {
choice is made deliberately to int value;
class A {
highlight the point that in C++, public: };
an object pointer named this is A(); void AConstructor(struct A* this) {
implicitly passed to a member int f(); this->value = 1;
private: }
function. int f_A(struct A* this) {
int value;
An object in C++ is simply }; return this->value;
a variable whose type is a C++ A::A() { }
class. It corresponds to a vari- value = 1; struct B {
} struct A a;
able in C whose type is a struct. int secondValue;
int A::f() {
A class is little more than the return value; };
group of member functions that } void BConstructor(struct B* this) {
operate on objects belonging to class B : public A { AConstructor(&this->a);
private: this->secondValue = 2;
the class. At the machine code }
int secondValue;
level, data is mostly made up of public: int g_B(struct B* this) {
objects and code is mostly made B(); return this->secondValue;
up of classes. int g(); }
}; void main() {
Clearly, arranging code into B b;
B::B() {
classes and data into objects secondValue = 2; BConstructor(&b);
is a powerful organising prin- } f_A ((struct A*)&b);
ciple. Dealing with classes and int B::g() { g_B (&b);
return secondValue; }
objects is inherently just as ef-
}
ficient as dealing with functions void main() {
and data. B b;
Listing 9
b.f(); Virtual functions.
b.g();
Constructors and Destructors // Classes with virtual functions
}
In C++, a constructor is a member class A {
private:
function guaranteed to be called int value;
when an object is instantiated or initialisation bugs and resource public:
created. This typically means the leakage. A();
compiler generates a constructor virtual int f();
};
call at the point where the object Inline Functions A::A() {
is declared. Similarly, a destructor Inline functions are a safer and value
is guaranteed to be called when more powerful substitute for C = 0;
an object goes out of scope. So a macros in many situations. It is }
int A::f() {
constructor typically contains any rare to see a macro that has local return 0;
initialisation that an object needs variables, and rightly so, because }
and a destructor does any tidying macros rapidly become illegible. class B : public A {
up needed when an object is no Inline functions, by contrast, have public:
B();
longer needed. the legibility and safety of ordi- virtual int f();
The insertion of construc- nary functions. Clearly, indiscrimi- };
tor and destructor calls by the nate use of inline functions can B::B() {
compiler outside the control of lead to bloated code, and novice }
int B::f() {
the programmer is something C++ programmers are invariably return 1;
that makes the C programmer cautioned on this point. }
uneasy at first. Indeed, program- However, appropriate use void main() {
ming practices to avoid exces- of inline functions can improve B b;
A* aPtr = &b;
sive creation and destruction of both size and speed. To estimate a->f();
so-called temporary objects are the code size impact of an inline }
a preoccupation of C++ pro- function, estimate how many
grammers in general. However, bytes of code it takes to imple- actual comparisons studying Operator Overloading
the guarantee that constructors ment it and compare that to generated code with optimi- A C++ compiler substitutes a
and destructors provide-that the number of bytes needed to sation turned on, you may be function call when it encounters
objects are always initialised and do the corresponding function surprised by how complex an an overloaded operator in the
are always tidied up-is generally call. Also consider that compiler inline function can profitably be. source. Operators can be over-
worth the sacrifice. In C, where optimisation can tilt the balance The break-even point is often far loaded with member or global
no such guarantees are provid- dramatically in favour of the beyond what can be expressed functions. So foo+bar is evaluated
ed, the consequence is frequent inline function. If you conduct in a legible C macro. to be operator+(foo, bar) or foo.

 eetindia.com | February 1998 | EE Times-India


operator+ (bar), which in terms of far, all confer substantial benefit at the class A constructor is called Inappropriate inheritance,
code generation amounts to the no run-time cost. first and the reverse happens with however, can make objects larger
same thing. Operator overload- destructors. than necessary. This is most likely
ing is a front end issue and can be Inheritance Listing 7 shows an example of to arise in class hierarchies, where
viewed as a function call for the In discussing how C++ imple- inheritance. Class B inherits from a typical class has several layers
purposes of code generation. ments inheritance, we will limit class A and adds the member of base class, each with its own
our discussion to the simple case function B::g() and the member member variables.
New and Delete of single, non-virtual inheritance. variable B::secondValue.
In C++, new and delete do the Multiple inheritance and virtual Listing 8 shows how this Virtual Functions
same job as malloc() and free() in inheritance are more complex and would be achieved in C. Struct Virtual member functions allow us
C, except that they guarantee con- their use is rare by comparison. B contains a struct A as its first to derive class B from class A and
structor and destructor calls. They Let’s consider the case in which member, to which it adds a vari- override a virtual member func-
also tend to be better-suited to class B inherits from class A. (We able secondValue. The function tion of A with one in B and have
frequent use with small quantities can also say that B is derived from BConstructor(struct B*) calls the new function called by code
of memory than older implemen- A or that A is a base class of B.) AConstructor to ensure initialisa- that knows only about class A.
tations of malloc() and free(). We now know what the inter- tion of its “base class.” Where the Virtual member functions provide
nal structure of an A is. What is the function main() calls b.f() in Listing polymorphism, which is a key fea-
Simplified String Class internal structure of a B? An object 7, f_A(struct A*) is called in Listing ture of OO design.
To illustrate the implementation of of class B is made up of an object 8 with a cast. Virtual functions have been
a class with the features we’ve dis- of class A, with the member data It is startling to discover that controversial. It would seem that
cussed, let’s consider an example of B tacked on at the end. In fact, the rather abstract concept of they exact a price for the benefit
of a simplified C++ class and its C the result is the same as if the B inheritance corresponds to such of polymorphism. Let’s see, then,
alternative. contains an A as its first member. a straightforward mechanism. how they work and what the
Listing 5 shows a string class Therefore, any member functions The result is that appropriately de- price is.
featuring a constructor and de- of class A called on an object of signed inheritance relationships Virtual functions are imple-
structor, operator overloading, class B will work properly. When have no run-time cost in terms of mented using an array of func-
new and delete, and an inline an object of class B is constructed, size or speed. tion pointers called a vtable for
function. each class that has virtual func-
Listing 6 is a C substitute for Listing 10 tions. Each object of such a class
the string class shown in Listing 5. C substitute for virtual functions. contains a pointer to the class’s
The inline function String::length() /* C substitute for virtual functions */ vtable. This pointer is put there
is replaced by the macro length_ struct A { by the compiler and is used by
String(). Operator overloads void **vTable; the generated code, but it isn’t
int value;
String::operator=(const char*) }; available to the programmer
and operator < < (ostream&, int) int f_A(struct A* this); and it can’t be referred to in
are replaced with function calls void* vTable_A[] = { the source code. Inspection of
operatorEquals_String_const_ (void*) &f_A objects with a low-level debug-
};
char_star(String*, const char*) and void AConstructor(struct A* this) { ger will reveal the vtable pointer,
operator ShiftLeft_ostream_un- this->vTable = vTable_A; if the reader is interested. Of
signed (ostream*, int) respectively. this->value = 1;
The constructor and destructor } Listing 11
int f_A(struct A* this) {
must then be called by the user of return 0; A C++ template.
the class, rather than being added } // Sample template class
automatically by the compiler. struct B { template
See how much harder to read A a; class A {
}; private:
the function main() is in Listing int f_B(struct B* this); T value;
6 than in Listing 5 and consider void* vTable_B[] = { public:
how much more danger there is (void*) &f_B A(T);
of a bug occurring. Consider how }; T f();
void };
much worse the problem would BConstructor(struct B* this) { template
be for a more realistic string class. AConstructor((struct A*) this); A
This is why C++ and the object this->a.vTable = vTable_B; ::A(T initial) {
paradigm are so much superior to } value = initial;
int f_B(struct B* this) { }
C and the procedural paradigm for return 1; template
partitioning complex problems. } T A
Next, consider that the code void main() { ::f() {
and data generated by Listing 5 struct B b; return value;
struct A* aPtr; }
is just as small and just as fast as BConstructor(&b); void main() {
that generated by Listing 6. It is typedef void (*f_A_Type)(struct A*); A
also safer, more coherent, more aPtr = (struct A*) &b; a(1);
readable, and more maintainable. ((f_A_Type)aPtr->vTable[0]) (aPtr); a.f();
} }
Of the C++ features discussed so

 eetindia.com | February 1998 | EE Times-India


course, the vtable pointer is kept
Listing 12 Listing 13
from the programmer for good
reasons, and using it directly is A C “template.” A C++ exception example.
an excellent way to prevent your /* C approximation of / C++ Exception example
template class */ #include
code being ported and to make
#define A(T) using namespace std;
its maintenance exciting! \ int factorial(int n) throw(const char*)
When a virtual member func- struct A_##T {
tion is called on an object, the \ if (n
{ <
generated code can use the ob-
\ 0)
ject’s vtable pointer to access the T value; throw
vtable for that class and extract \ “Negative Argument to factorial”;
the correct function pointer. That }; if (n>0)
\ return n*factorial(n-1);
pointer is then called.
\ return 1;
Listing 9 shows an example void AConstructor_##T(A_ }
using virtual member functions. ##T* this, void main()
Class A has a virtual member func- \ {
T initial) try
tion f(), which is overridden in class
\ {
B. Class A has a constructor and a { int n = factorial(10);
member variable, which are actu- \ cout
ally redundant, but are included (this)->value = initial; <
\ <
to show what happens to vtables
} “factorial(10)=”
during object construction. \ <
Listing 10 shows what a C \ <
substitute would look like. The T A_f_##T(A_##T* this) n;
\ }
result is both extremely ugly
{ catch (const char* s)
and hazardous. In the last line of \ {
main(), we see the virtual function return (this)->value; cout
call, which, after all the casting, \ <
} <
uses the object’s vtable pointer to
A(int) /* Macro expands
look up the vtable of its class for to ýclass’ A_int */ “factorial threw exception : “
the function pointer.
Let’s quantify the costs of vir- void main() { <
A_int a; <
tual functions, in order of prior-
AConstructor_int(&a, s
ity. The first cost is that it makes 1); <
objects bigger. Every object of a A_f_int(&a); <
class with virtual member func- } “\n”;
}
tions contains a vtable pointer.
}
So each object is one pointer (to get the object’s vtable pointer)
bigger than it would be oth- and a second memory read (to that it will be linked, even if it isn’t source file where it’s used. Newer
erwise. If a class inherits from get the function pointer from the used in a particular system. compilers and linkers, however, find
a class that already has virtual vtable). This cost has been the So the bottom line on virtual duplicates and produce at most
functions, the objects already subject of heated debate and it functions is that they have little one expansion of a given template
contain vtable pointers, so there is hard to believe that the cost is impact on speed, but be aware with a given parameter class.
is no additional cost. But adding typically less than to that of adding of their effects on code size and Used appropriately, templates
a virtual function can have a dis- an extra parameter to a function. data size. Virtual functions are not can save a lot of effort at little or
proportionate effect on a small We hear no arguments about the mandatory in C++, unlike in other no cost. After all, it’s a lot easier
object. An object can be as small performance impact of additional OO languages. and generally more efficient to
as one byte and if a virtual func- function arguments because it use complex from the Standard
tion is added and the compiler is generally unimportant, just as Templates C++ Library, rather than write
enforces four-byte alignment, the cost of a virtual function call is C++ templates are powerful, as your own class.
the size of the object becomes generally unimportant. shown by their use in the Standard Listing 11 shows a simple
eight bytes. But for objects that A less discussed cost of virtual C++ Library. A class template is template class A. An object of
contain a few member variables, functions is their impact on code rather like a macro which produces class A has a member variable of
the cost in size of a vtable pointer size. Because each class with virtual an entire class as its expansion. class T, a constructor to initialise
is marginal. functions has a vtable containing Because a class can be produced and a member function A::f() to
The second cost of using vir- pointers to all its virtual functions, from a single statement of source retrieve it.
tual functions is the one that gen- the pointers in this vtable must be code, misuse of templates can The macro A(T) in Listing
erates the most controversy. That resolved by the linker. This means have a devastating effect on code 12 approximates a template
is the cost of the vtable lookup for that all virtual functions of all size. Older compilers will expand class in C. It expands to a struct
a function call, rather than a direct classes used in a system are linked. a templated class every time it is declaration and function defini-
one. The cost is a memory read be- Therefore, if a virtual function is encountered, producing a differ- tions for functions correspond-
fore every call to a virtual function added to a class, the chances are ent expansion of the class in each ing to the constructor and the

 eetindia.com | February 1998 | EE Times-India


suggests an association with purer more member functions than a C
Listing 14
OO languages like Smalltalk. This programmer would expect to use.
A C “exception” example. association causes anxiety among This is because properly designed
/* C approximation of exception handling */ the performance-conscious that classes are complete and contain
#include
efficiency has been compromised member functions to do anything
#include
jmp_buf ConstCharStarException; for purity. This is not so. Run-time with objects belonging to the
const char* ConstCharStarExceptionValue; type information exploits the class that might legitimately be
int factorial(int n) vtable pointer in an object that needed. For a well conceptualised
{
has one and provides sensible de- class, this number will be reason-
if (n
< faults for an object that does not. If ably small, but nevertheless larger
0) you don’t use run-time type infor- than what the C programmer is
{ mation, the only run-time cost is accustomed to.
ConstCharStarExceptionValue =
that classes are a little larger. If you When calculating code size,
“Negative Argument to factorial”;
longjmp(ConstCharStarException, 0); use a compiler option to disable bear in mind that modern linkers
} run-time type information, that designed for C++ extract from
if (n>0) cost is avoided. object files only those functions
return n*factorial(n-1);
that are actually called, not the
return 1;
} Memory Considerations entire object files. In essence,
void main() Having discussed the implemen- they treat object files like librar-
{ tation of the major C++ language ies. This means that non-virtual
if (setjmp(ConstCharStarException)==0)
features, we can now evaluate class member functions that
{
int n = factorial(10); C++ in terms of the machine code are unused have no code size
printf(“factorial(10)=%d”, it generates. Embedded systems penalty. So a class that seems to
n); programmers are particularly have a lot of baggage in terms
}
concerned about code and data of unused member functions
else
{ size, so we need to discuss C++ in may be quite economical in
printf( these terms. practice.
“factorial threw exception : %s\n”, In the case of virtual functions,
ConstCharStarExceptionValue);
How big is a class? it is reasonable to assume that all
}
} In C++, most code is in class mem- virtual functions of all classes used
ber functions and most data is in in a system will be linked into the
member function. We can see normal case is somewhat at the objects belonging to these class- binary. But class completeness in
that although it’s possible to expense of performance in the es. C++ classes tend to have many itself does not lead to code bloat.
approximate templates in C, it abnormal case. The second factor
Listing 15
is impractical for any significant is the run time of destructor calls
functionality. between an exception being A C ROMable dictionary.
thrown and being caught. /* A C ROMable dictionary */
Exceptions Because of the performance #include
typedef struct
Exceptions are to setjmp() and penalty in the no exceptions case, {
longjmp() what structured pro- many compilers have a “no excep- const char* englishWord;
gramming is to goto. They im- tions” option, which eliminates ex- const char* foreignWord;
pose strong typing, guarantee ception support and its associated } DictEntry;
const static DictEntry germanDict[] =
that destructors are called, and performance cost. {
prevent jumping to a unused Listing 13 shows an example {“yes”, “ja”},
stack frame. of an exception and Listing 14 {“no”, “nein”},
Exceptions are intended to shows a C substitute that has sev- {NULL, NULL}
};
handle conditions that don’t eral shortcomings. It uses global const static DictEntry frenchDict[] =
normally occur, so implementa- variables. It allows longjmp(Cons {
tions are tailored to optimise per- tCharStarException) to be called {“yes”, “oui”},
formance for the case when no either before it is initialised by se {“no”, “non”},
{NULL, NULL}
exceptions are thrown. Support tjmp(ConstCharStarException) or };
for exceptions results in a small after main() has returned. In addi- const char* FromEnglish(
performance penalty for each tion, substitutes for destructor calls const DictEntry* dict,
function call. (This is to record must be done by the programmer const char* english);
const char* ToEnglish(
information to ensure destructor before a longjmp(). There is no const DictEntry* dict,
calls are made when an excep- mechanism to ensure that these const char* foreign);
tion is thrown.) The time taken calls are made. /*... */
to throw an exception is unpre- void main()
{
dictable and may be long due to Run-time Type Information puts(FromEnglish(frenchDict, “yes”));
two factors. The first is that the Run-time type information is a }
emphasis on performance in the recent addition to C++. Its name

 eetindia.com | February 1998 | EE Times-India


How big is an object? in ROM. For a system written in C,
Listing 16
The size of an object can be cal- this means that all the non-vary-
culated by examining its class ing data known at compile time A C++ ROMable dictionary NOT!
(and all its base classes). Ignore can be specified by static initialis- // NOT a ROMable dictionary in C++
#include
member functions and treat the ers, compiled to be stored in ROM
using namespace std;
data the same as for a struct. and left there. class Dict
Then add the size of a pointer if In C++, we can do the same, {
there are any virtual functions in but we tend not to. In well de- public:
Dict();
the class or base classes. You can signed C++ code, most data is
const char* fromEnglish(
confirm your result by using the encapsulated in objects. Objects const char* english) const;
sizeof operator. It will become ap- belong to classes and most class- const char* toEnglish(
parent that the combined size of es have constructors. The natural const char* foreign) const;
private:
objects in a system need be no OO equivalent to const initialised
enum { DictSize = 3 };
greater than the size of data in a data is a const object. A const struct
C-based procedural model. This static object that has a construc- {
is because the same amount of tor must be stored in RAM for its const char* english;
const char* foreign;
state is needed to model a system constructor to initialise it. So while
} table[DictSize];
regardless of whether it is organ- in C a const static object occupies };
ised into objects. cheap and plentiful ROM, its natu- // *** Following won’t compile ***
ral heir in C++ occupies expensive const static Dict germanDict =
{
C++ and the Heap and scarce RAM. Initialisation is
{
Heap usage is much more com- performed by start-up code that {“yes”, “ja”},
mon in C++ than in C. This is calls static constructors with pa- {“no”, “nein”},
because of encapsulation. In C, rameters specified in declarations. {NULL, NULL}
}
where a function requires an This start-up code occupies more
};
unknown amount of memory, ROM than the static initialiser // *** Following won’t compile ***
it is common to externalise the would have. const static Dict frenchDict =
memory as an input parameter So if a system includes a lot {
{
and leave the user with the prob- of data that can be kept in ROM,
{“yes”, “oui”},
lem. This is at least safer than mal- special attention to class design {“no”, “non”},
loc ing an area and relying on the is needed to ensure that the rel- {NULL,
user to free it. But C++, with its evant objects are ROMable. For an NULL}
}
encapsulation and destructors, object to be ROMable, it must be
};
gives class designers the option capable of initialisation by a static //...
(and responsibility) of managing initialiser like a C struct. Although void main()
the class heap usage. the easy way to do this is to make {
cout
This difference in philosophy is it a simple C struct (without
<
evident in the difference between member functions), it is possible <
C strings and a C++ string class. to make such a class a bit more germanDict.fromEnglish(“yes”);
In C, you get a char array. You object-oriented. }
have to decide in advance how The criteria for a static initialiser
long your string can be and you to be allowed for a class are: of the OO ideal of a class that is to none of the restrictions that the
have to continuously make sure • The class must have no base easy to use correctly and difficult ROMable class is, so we can put in
it doesn’t get any bigger. A C++ classes to use incorrectly. The unwary it a proper restricted interface to
string class, however, uses new • It must have no constructor class user can, for example, our const static data.
and delete to allow a string to be • It must have no virtual declare and use a non- const, To illustrate this discussion,
any size and to grow if necessary. functions uninitialised instance of the class let's consider a simplified example
It also makes sure that the heap • It must have no private or pro- that is beyond the control of the of a hand-held electronic multi-
is restored when the string is no tected members class designer. language dictionary. To keep it
longer needed. • Any classes it contains must To let us sleep securely in our simple, the translator handles
The consequence of all this obey the same rules OO beds, something more is translation to German or French
is that you can scrape by in an needed. That "something" is class and it has a vocabulary of two
embedded system written in C In addition, we should also nesting. In C++, we can declare words, "yes" and "no." Obviously,
without implementing malloc require that all member functions classes within classes. We can take these dictionaries must be held
and free, but you won’t get far in of a ROMable class be const. A C our dubious class that is open to on ROM. A C solution would be
C++ without implementing new struct meets these criteria, but misuse, and put it in the private something like Listing 15.
and delete. so does a class that has member segment of another class. We can
functions. Although this solves also make the const static instanc- A Dict is an array of DictEntry.
ROMable Objects the ROMability problem and en- es of the dubious class private A DictEntry is a pair of const char*
Linkers for embedded systems hances the C struct with mem- static members of the encapsulat- pointers, the first to the English
allow const static data to be kept ber functions, it falls far short ing class. This outer class is subject word, the second to the foreign

 eetindia.com | February 1998 | EE Times-India


Now, let’s do it right. In Listing 40; and “Embedded C++,” C/C++
Listing 17
18, the class Dict in Listing 17 be- User’s Journal, February 1997, p.
A C++ ROMable corruptable dictionary. comes DictTable, which is nested 35).
// A ROMable dictionary in C++, privately within the new class Dict.
// but with poor encapsulation
Class Dict also contains, as a static Class Libraries and Embedded
#include
using namespace std; member, an array of DictTables, Systems
class Dict which we can initialise statically. A major benefit of using C++ is the
{ The function main() shows use of availability of class libraries and the
public:
this class Dict, which has a clean productivity gains they promise.
const char* fromEnglish(
const char* english) const; interface. So to make the best use OO design makes class libraries
const char* toEnglish( of OO design for data on ROM, much easier to use (and harder to
const char* foreign) const; special class design is needed, misuse) then their procedural pre-
// PLEASE don’t access anything in the class
which is quite unlike the casual decessors. But a suspicion exists
// below this comment.
// PLEASE don’t create your own instances approach typical of C. that class libraries are large and of
// little use, and that while this may
of this class. EC++ be tolerable on a desktop PC, class
enum { DictSize = 3 };
As we’ve seen, some C++ features libraries are generally unsuited to
struct
{ have costs that may be undesir- embedded systems.
const char* english; able in embedded systems. Could This is a misplaced generalisa-
const char* foreign; a subset of C++ be more cost-ef- tion. If a judgement is made on
} table[DictSize];
fective in embedded systems? the basis of so-called applica-
};
const static Dict germanDict = Which subset? Programmers who tion framework libraries, such
{ use ad hoc subsets are frustrated as Microsoft Foundation Classes
{ in importing libraries, an ironic (MFC), it is easy to see how this
{“yes”, “ja”},
consequence of adopting a lan- opinion is formed. Application
{“no”, “nein”},
{NULL, NULL} guage which offers such promise frameworks are designed to do
} for libraries. Wouldn’t a widely as much as possible of an applica-
}; recognised subset be better? tion, allowing the programmer to
const static Dict frenchDict =
Class library vendors could supply concentrate on the specifics of his
{
{ products written in the subset. or her project. This is usually done
{“yes”, “oui”}, Compiler vendors could enforce by inheriting from classes in the
{“no”, “non”}, it and take advantage in code library to produce specialised be-
{NULL, NULL}
generation of the known absence haviour. Most of the functionality
}
}; of troublesome features. This was of the classes in the framework are
//... the motivation behind Embedded not the concern of the program-
void main() C++ (EC++), a de facto standard mer using them.
{
subset of C++. Embedded C++ While this type of class library
cout
< is the same as C++ except that is well suited to its intended use,
< it prohibits multiple inheritance, small code size isn’t its best fea-
germanDict.fromEnglish(“yes”); templates, exceptions, namespac- ture, and its narrow focus makes
}
es, and run-time type identifica- it essentially useless outside its
tion. Because it’s a strict subset target application area. An em-
word. The end of a Dict is marked a clean and simple interface. of C++ with no extensions, it can bedded system needs a more
by a DictEntry containing a pair of Unfortunately, Listing 16 won’t be compiled with an existing C++ general purpose class library that
NULL strings. compile. The static initialisers compiler. can help with a variety of pro-
To complete the design, we for frenchDict and germanDict EC++ shouldn’t be an auto- gramming problems. (Indeed, the
add a pair of functions which try to access private members of matic choice for embedded sys- same could be said of any system
perform translation from and to the objects. tems programming. While most of whose implementation isn’t domi-
English using a dictionary. This is a If we make these members its restrictions are easy to accept, nated by the needs addressed by
simple design. The two dictionar- public and eliminate the construc- forswearing templates and the a framework.) The characteristics
ies and the strings to which they tor as in Listing 17, the class will Standard C++ Library shouldn’t of a class library suitable for em-
point reside in ROM. meet the criteria for static initialis- be done lightly. There is no reason bedded systems are as follows.
Let’s now consider what ers and the code will compile, why the full language can’t be It should include classes such
happens if we produce a naýve but we’ve broken encapsulation. used in embedded systems. But as strings and container classes
OO design in C++. Looking at Users can see the internal imple- if you do decide to use a subset, like lists, vectors, hash tables,
Listing 15 through OO glasses, mentation of the class and bypass EC++ would be better supported maps and sets, and more. Such
we identify a class Dict with the intended access functions. than an ad hoc subset. a class library can dramatically
two member functions const Even worse, they can create their For more information about shrink and improve code that in
char* Dict::fromEnglish(const own (con-const) instances of Dict EC++, see P.J Plauger’s articles on C manipulates char* s and arrays.
char*), and const char* Dict:: whose internal state is outside our the subject (“Embedded C++: An Classes in the library should
toEnglish(const char*). We have control. Overview,” ESP, December 1997, p. be independent. This implies

 eetindia.com | February 1998 | EE Times-India


limited use of inheritance. This libraries exist, though none has embedded systems are starting of modern embedded systems
characteristic is in contrast with been used as much as MFC. They to emerge, most notably a revi- development. Unfortunately, we
a framework in which most include the draft Standard C++ sion of the Standard C++ Library are not yet at the stage where a
classes are in an inheritance hier- Library, gnu’s libg++, and Rogue for embedded systems. C++ cross compiler from an es-
archy. Independence allows the Wave Tools.h++. None are tai- Compilers and Tools for tablished vendor can be assumed
programmer to treat the library lored to embedded systems, but Embedded Systems to produce good code quality and
like an a la carte menu, while a all are better models of a class Most C cross compiler vendors be supported by a good robust
framework is more like a dietary library for embedded systems for 32-bit targets now offer C++ tool set, so it is necessary to be
regime. than a framework library. Class compilers as well. The older com- cautious about tool investments.
Several general purpose class libraries specifically designed for pilers among them are based on
AT&T’s cfront. Cfront was the origi- Reality Check
Listing 18 nal C++ compiler and it produces Having minutely examining the
A clean C++ ROMable dictionary. C code output, which is then fed costs of C++ features, it is only
#include to a C compiler. Naturally, cfront fair to see how C stands up to the
using namespace std; was an easy option for estab- same degree of scrutiny. Consider
class Dict lished C compiler vendors who the C code in Listing 19. One line
{
public: were looking for a C++ offering. contains a floating-point value,
typedef enum But cfront has a poor reputation which will pull in parts of the
{ in terms of efficiency and optimi- floating-point library and have a
german, sations, and it is understandable disproportionate effect on code
french
} Language; that a compiler whose mission it size, if floating point isn’t needed.
Dict(Language lang); was to prototype the language The next line will have a similar
const char* fromEnglish( should suffer from these limita- effect, if printf had been avoided
const char* english) const; tions. Unfortunately for the ac- elsewhere. The next line calls
const char* toEnglish(
const char* foreign) const; ceptance of C++, opinions have strlen(s) strlen(s) times, rather
private: been formed on the basis of than once, which has a serious
class DictTable experience with this compiler, impact on run time.
{ which compared badly with ma- But who would argue that
public:
const char* fromEnglish( ture, optimising C compilers. these abuses are reasons not
const char* english) const; to use C in embedded systems?
const char* toEnglish( The Free Software Similarly, it is wrong to brand
const char* foreign) const; Foundation’s g++, which is well C++ as unsuitable for embed-
enum { DictSize = 3 };
struct established as a native compiler ded systems because it can be
{ that generates excellent machine misused.
const char* english; code on several platforms, has
const char* foreign; been used successfully as a cross Bigger than a bread box?
} table[DictSize];
}; compiler. There is no need for a system
const static DictTable Several compiler vendors now implemented in C++ to be larger
DictTables[]; offer improved C++ compilers than a C implementation. Most
Language myLanguage; in a package with source-level C++ features have no impact on
};
const Dict::DictTable Dict::DictTables[]= debugging and other necessities code size or on speed. Some C++
{ features have a minimal impact in
{ Listing 19 these areas, and they have been
{“yes”, “ja”}, discussed out of proportion to
{“no”, “nein”}, C reality check.
{NULL, NULL} /* Reality check - */ their significance. Using C++ ef-
}, /* some things to avoid fectively in embedded systems re-
{ in C */ quires that you be aware of what
{“yes”, “oui”}, #include is going on at the machine code
{“no”, “non”}, #include
{NULL, NULL} void main() level, just as in C. Armed with that
} { knowledge, the embedded sys-
}; char s[] = “Hello tems programmer can gain great
//... world”; benefits from using C++, while
void main() unsigned i;
{ int var =1.0; instinctively avoiding the pitfalls
Dict germanDict (Dict::german); printf(s); that intimidate the novice.
cout for (i=0; i
< <
< strlen(s); i++)
germanDict.fromEnglish(“yes”); /*... */
} }
Email   Send inquiry

 eetindia.com | February 1998 | EE Times-India

You might also like