Dive in to C++ and survive - Embedded.com

Dive in to C++ and survive

If you're anything like me, when you run into a little programmingproblem you just trot down to the library and pick up a couple ofbooks/manuals on whatever it is you need to master. Do a little readingand POW! You've got the tools you need.

When I ran into problems with my first major C++ project, however, Idiscovered that each new “solution” just slammed me into anotherroadblock. I needed answers. But all I got was frustration.

And it looks like I'm not the only one. According to the 2008Embedded Market Study, C++ use has lagged behind C and thenumbers have not significantly improved. In 2005, only 26% of thosesurveyed used C++ (compared to 51%with C ) and while C use rose about six percentage points overthe next three years, C++ use fluctuated before it settled into a merethree percent increase.

No surprise, given the frustration factor, but the fact of thematter is C++ is an amazingly useful tool in embedded design. Butbefore you can use its awesome powers, you just have to wrap your mindaround one little detail:   C++ isn't a programminglanguage; it's a worldwide research project running about in disguise.

Here's the lowdown. Reading compiler manuals will only get you sofar. The bulk of C++ is documented in papers, presentations, and (most importantly) the experience ofits users. In fact, the range of legal C++ code is so wide that youhave to write idiomatically to be understood by other programmers (see References [2, 3, 4, 5]).You want to be good? Then you've got to get involved.

So, come on. Let's dive in and get our hands dirty.

Many of us remember when the number one (frustrating) feature of C++ wasthat its portable code wasn't actually portable. These days, C++ ismuch more portable on desktop and embedded platforms, but portabilitystill isn't a given [7].

Your best bet? You got it – head to the library! C++ library authorsand vendors are C++'s QC department. To see how your compiler measuresup, start with the Boost library regression test summary [8] and the Dinkumwarestandard-library manual [10] .

For many platforms [11] , GNUGCC is a good bet for a conforming compiler with a price that justcan't be beat. If there's no C++ compiler for your platform, try ComeauC++ [9] . It claims to trackthe standard very closely, and compiles C++ to C for your favoritetoolchain. While I haven't tried it myself, the reviews are good.

The compiler's main business is managing names, not generatingassembly. The code int i; doesn't necessarily allocate space for aninteger. Instead it gives the name i a new property: it can refer tosomething of int type in the current scope.

This matters when you use overloading and templates, when what aname refers to depends on the context in which it is used. Templatematching and overload resolution are designed to make the common casesJust Work, but this can lead to confusing errors about ambiguousfunction calls (Appendix B in [11]).

Partial specialization, where a template can behave differently forsubgroups of its arguments, adds even more power with its attendantcomplexity (Sections 1.5 and 2.2 in [6] ). Unfortunately, there'sno shortcut here, so start simply and learn the rules as you go.

The happy news if you're using a good library (whether yours orsomeone else's), is that C++ is pretty close to “Do What I Mean.”However, coding libraries is very different from coding applications.Take a look at your compiler's Standard Template Library (STL)implementation. Library code is some of the densest, gnarliest codethere is. In fact, most of the fanciest features in C++ are intendedonly for writing libraries (Page xiiiin [1] ).

Now, you might think that all these fancy features would make forbloated libraries; bit before you start worrying, remember that C++libraries are generally designed to optimize the most common case.

While you do have to be careful, using a library doesn'tautomatically cause bloat and slowdown. For example, inlining is thecompiler's job”not the library's”so you control code size with compileroptions. As always, the trade-off is code size versus memory or maximumstack depth.

At its core C++ is a multiparadigm language. To get the bestsolution to a problem, you generally combine elements ofobject-oriented, procedural, and (sometimes) functional languages.

The great part is that it's designed around the zero-overhead rule;you don't pay in space or time for any feature you don't use [12]. Consequently, C++ supportslots of different styles that you can mix and match.

Once again it must be said that the most challenging stumblingblocks of C++ are also some of its best features. Take friend functions(the standalone functions that haveaccess to the implementation details of a class ).

At first, I thought they damaged a clean object-oriented design byintroducing misplaced procedural code. As it turns out, however, whenproperly used, friends are not an import into a class, they are part ofthe class that happens to be called as func(obj)instead of obj.func() .

Friend functions give you the freedom of more a natural syntax (Pages 217-218 in [1]). Sutter calls this the”Interface Principle”: “all functions, including free functions, thatboth 'mention' [aclass] X [and] are 'supplied with' X are logically part of X” (Page 123 in [2]).

The use of friends highlights an important general point aboutclasses: in C++, classes are primarily for encapsulation, notinheritance. Inheritance is a special-purpose tool. When writing aclass, first ask how to make it look like part of the language, ratherthan how to inherit from it.

You can use the Rule of Three: copy constructors, assignmentoperators, and destructors go together (Section 11.3.6 in [1]). A class with “valuesemantics” (one that behaves like an integer) is free of lots ofpotential allocation and parameter-passing bugs.

Up until recently, I saw the compiler as just another tool ” usefulbut not exactly world-changing. Yet in C++, a compiler cannot only dotremendous amounts of compile-time processing, it can check atcompile-time things that once had to be checked at run-time!

It's like a second shell. Class templates and specializations form afunctional language of their own that you can use to do integercalculations, and even manipulate types at compile-time [6]. The boost library even doesamazing things with the preprocessor. Sure, you have to rely moreheavily on your compiler's optimizer, but you can design on a muchhigher level than you ever thought possible.

Don't look at C++ as a just long line of frustrations”get yourselfoutside the box and see the possibilities. C++ excels at large-systemdesign. You can reuse code on a very high level, and still get thelow-level control you need for an embedded system.

While you can bet that I'm not going to try C++ on a PIC anytimesoon. But as embedded devices grow in their complexity, there's moreand more reason to dive in and see what C++ can do for you, as the twopages of idiom examples that follow illustrate.

Idiom Examples
Listing 1 below shows one ofthe most common and useful C++ idioms: Resource Acquisition IsInitialization (RAII). If the constructor of anobject (Lock::Lock )acquires a resource (mutex ) and thedestructor (Lock::~Lock )releases that resource, the compiler guarantees you'll never leave therelevant code region without releasing that resource. This eliminatesmany concurrency errors and leaks.

Listing 1 — RAII: resource acquisition is initialization

#include using namespace std;

class Lock {public: typedef void *mutex_t; //void* is a placeholder typeprivate: mutex_t& mutex_;public: Lock(mutex_t& new_mutex): mutex_(new_mutex) { cout < "taking="" lock="" "="">< &mutex_="">< endl;="" }="" ~lock()="" {="" cout="">< "releasing="" lock="" "="">< &mutex_="">< endl;="" }="" };="" lock="" int="" main()="" {="" lock::mutex_t="" mutex;="" lock="" l(mutex);="" cout="">< "hello,="" world!"="">< endl;="" return="" 0;="" }="">

Output, listing 1

Taking lock 0x22ccccHello, World!Releasing lock 0x22cccc   

Note: if copying code out of this article, be aware that some great-than and less-than symbols were typeset in html and need to be replaced. Click here for a screenshot of the listing.

RAII really shines with exception handling. Exceptions separateerror-handling logic from program logic, but doing it right is tedious.Listing 2a below shows aprogram that does memory allocation and handles std::bad_allocexceptions, which could happen any time you try to allocate heap.

Listing 2a – Exception handling, hardcoded

void hardcoded_recovery(){  int *pa = NULL;		//Initialize to NULL so that  int *pb = NULL;		//operator delete will not try  int *pc = NULL;		//to delete memory never allocated  int *pd = NULL;  try {    pa = new int;    pb = new int;    throw bad_alloc();	//demo    pc = new int;    pd = new int;    *pa = 2;  }   catch(bad_alloc&)  {    cout < "in="" handler"="">< endl;="" delete="" pd;="" any="" or="" all="" could="" have="" been="" allocated,="" delete="" pc;="" so="" all="" have="" to="" be="" deleted.="" delete="" pb;="" delete="" pa;="" }="" }="">   

Click here for a screenshot of authors' original typsetting of code.

Listing 2b below , usinghelper class IntHolder (Listing 2c below ), is muchsimpler. IntHolder is an example of a handle class (Chapter14 in [1] ), a safewrapper class written to hold an object that requires careful handling.

Listing 2b — Exception handling, RAII: application code

void RAII_recovery(){ //An example of simple application code using a library.  try {    IntHolder a, b, c, d;	//E.g. assume c throws    a = 2;			//Use operator int&  }  catch(bad_alloc&)  { //c threw, so a and b free what they allocated.    //c threw and d was never allocated, so neither exists,    //and therefore neither needs cleanup.    cout < "in="" handler"="">< endl;="" }="" }="">   

Click here for a screenshot of authors' original typsetting of code.

Listing 2c — Exception handling, RAII: library code

class IntHolder{ //A "handle" class which will make an int* safe.  //This is an example of (possibly-complex) library code.private:  int *data_;public:  IntHolder(): 	data_(NULL) 		//Pre-init to NULL (optional).	{ data_ = new int; }	//Allocate.		// If the new throws, the exception will		// leave IntHolder and the IntHolder object will 		// never be created.

~IntHolder() { delete data_; } //This will only run if data_ was allocated successfully. operator int& () { return *data_; } //Make it look like an int};

Click here for a screenshot of authors' original typsetting of code.

Caution: if a program (or thread) terminates becauseof an unhandled exception, all bets are off. On Microsoft Visual C++6.0, the program immediately terminates without doing any cleanup atall.

On GCC 3.4.4, it dumps core. To prevent this, make the main functionof any application or thread likeListing 2d below use a catch(…) to grab all unhandledexceptions. Even if the catch body is a no-op, the catch will make surethe exception handlers run as they should.

Listing 2d — Preventing exceptions from escaping

int main_function(){  try {    //Do useful things  } catch(...) {  // Don't omit this!    cout < "caught="" unhandled="" exception"="">< endl;="" }="" return="" 0;="" }="">   

Click here for a screenshot of authors' original typsetting of code.

More idiom examples
Another useful C++ idiom is the pimpl (Pages 99-118 in [2] ). In C++, the privatedeclarations of a class are visible in the header file for that class.That means information that should be private is really public, andthat you have to recompile anything that uses that class whenever youtweak what's under the hood.

Listing 3a — pimpl.h

class Interface{private:  class Implementation;		//no-one else can see it  Implementation *pimpl_;public:  Interface();  ~Interface();  void Frob();}; //Interface   

Click here for a screenshot of authors’ original typsetting of code.

One solution is to move your private implementation into a separateclass and store only a pointer to that class: a Pointer toIMPLementation. Listing 3a above shows the header file, and Listing 3bbelow the implementation, for a simple class with a pimpl. File pimpl.h declaresInterface::Implementation to be a class, but gives no details.

Listing 3b — pimpl.cpp

#include "pimpl.h"

class Interface::Implementation{ //Interface:: because it is nested in class Interfacepublic: Implementation() { cout < "implementation="" constructor"="">< endl;="" }="" void="" frob()="" {="" cout="">< "implementation::frob"="">< endl;="" }="" };="">

Interface::Interface(): pimpl_(NULL){ pimpl_ = new Implementation();}

Interface::~Interface(){ delete pimpl_;} void Interface::Frob(){ pimpl_->Frob(); //The Implementation does the work}

Click here for a screenshot of authors’ original typsetting of code.

File pimpl.cpp provides the definition of Implementation and whatever of thedefinition of Interface wasn't in pimpl.h . Interface allocates and deallocates an Implementation using RAII. then uses Interface::Frob()Implementation::Frob() to do the work.

The space overhead of a pimpl is anextra object on the heap, typically from four to 32 bytes (Page 78 in [6] ). The time overhead isthat every call turns into two: one into the interface and one into theimplementation. For large systems, it's worth paying the price.

A final caution : Always include a defaultconstructor in the interface classes (Interface::Interface() ).If you don't, the compiler will generate one that wants the details ofImplementation. It won't find the details, so compilation will fail.

Chris White is an embedded-systemsdesigner and intellectual-property manager working on organic LEDdisplays for Eastman Kodak Company in Rochester, NY. He can becontacted by email at cxwembedded@gmail.com .

My Annotated Bibliography(Read First Resources)
1. Koenig, Andrew, and Moo,Barbara E. AcceleratedC++: Practical programming by example. Boston:Addison-Wesley, 2000. (Author's note: Read this first; it introduces C++ [fromzero through templates, user-defined conversions and polymorphism] inmodern-C++ style. )

2. Sutter, Herb. Exceptional C++ : 47 engineeringpuzzles, programming problems, and solutions. Boston: Addison-Wesley,1999.

3. Sutter, Herb. More Exceptional C++: 40 newengineering puzzles, programming problems, and solutions. Boston:Addison-Wesley, 2002.

(Author's Note : Read the two Exceptional C++ booksbetween Accelerated C++ above and Modern C++ referenced below. Theseare essentially idiom catalogs that will help you write more robust,maintainable programs. They are very readable and very informative. See also Herb Sutter's GotW.ca Home Page . )

4. Cline, Marshall. “C++ FAQ Lite – Frequently Asked Questions,”. (Author's note: The first place to look online. )

5. Gamma, Erich, Helm,Richard, Johnson, Ralph, and Vlissides, John. Design Patterns: elements of reusableobject-oriented software. Reading, Massachusetts:Addison-Wesley, 1995. The Gang of Four (GoF) catalog of designpatterns. (Author's Note: Read this once you're comfortablewith inheritance and polymorphism. Includes an example or two for eachpattern, but there's no substitute for practice. )

6. Alexandrescu, Andrei. Modern C++ Design: Generic programming anddesign patterns applied . Boston: Addison-Wesley, 2001. (Author'sNote : Has to be seen to be believed. Read this either when you'refluent in inheritance and templates and want to combine the two, orwhen you want to know why you should become fluent.

(Other useful C++ Resources)
7 . Becker, Thomas. “On the Tension Between Object-Oriented andGeneric Programming in C++ and What Type Erasure Can Do About It,”C++ Source.

8. Boost Project. “Testing,” . .

9. Comeau C++ homepage . .

10. Dinkumware Ltd. “Dinkum Compleat Libraries: Introduction.

11. GNU Project. “HardwareModels and Configurations .”  The targetssupported by gcc.

12. Stroustrup, Bjarne. “Evolving a language in and for the realworld: C++ 1991-2006.”  .

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.