C++ in embedded systems - Page 2

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View
Re: C++ in embedded systems
Quoted text here. Click to load it

One thing I forgot to mention: I am using the traditional C/asm
combination. I don't believe C++ brings anything to the party except
additional headaches. I haven't read any conclusive proof that C++ is
more maintainable than C, and my personal feeling is that C++ is
slower to develop and harder maintain.

Re: C++ in embedded systems

Quoted text here. Click to load it

Yes it is controversial. Whether you accept it or not, the OO approach
is the current vogue. Graduating Software Engineers are conversant
with OOA & OOD. A common language, UML, has been developed so that
analysis & design can be better expressed. There are a plethora of OO
tools available now.

Also why is software developed for embedded system any different in
quality  requirements from that of other fields? It isn't. The same
process that is adopted in the OO methodology is applicable for
embedded development. The name of the game is to produce "quality"
software. OO in itself doesn't guarantee this, but it's the associated
process & approach (in using modelling) that empowers the Software
Engineer with the ability to achieve this goal.

Quoted text here. Click to load it

Don't quite understand this comment.

Quoted text here. Click to load it

I disagree. Sure for embedded systems there is an association between
the hardware & software, but like any software system the hardware can
be mapped to drivers. I can imagine that one can write software for an
embedded system where hardware access is spaghetti-ed throughout the
code. If you do, then one is not writing their software in a fashion
that would make it easily testable. Sure one has In-Circuit Emulators
& alike, but I regard them as belated options in testing.

Where I work, we write the software such that it can be readily ported
to other platforms which may employ different micros and/or different
hardware and/or different operating systems. To remotely achieve this,
one has to delineate the hardware from the software application. Also
I'm not talking about monster applications but embedded systems based
on micros like Hitachi's SH-1, Tiny H8 & Motorola's 6805.

In a current project with the Tiny H8, we are achieving about 98%
structural (branches & conditional) unit test coverage of the Software
Application, with a test harness that runs in a console window on a PC
and about 95% structural unit test coverage of drivers that run on the
target system. That's the current status, but our goal is to achieve
100% coverage -- which we expect to do. The integrated system does fit
& run on a Tiny H8, but unfortunately, there isn't enough ROM space to
accomodate a full test harness on the target system & so it has to be
broken up.

Ken.

Quoted text here. Click to load it


+====================================+
I hate junk email. Please direct any
genuine email to: kenlee at hotpop.com

Re: C++ in embedded systems
Quoted text here. Click to load it

I agree with everything you say.  However you appear to make the
erroneous assumption that OO is the only and best methodology available.
  it is neither in my view.

Quoted text here. Click to load it

I agree.  But a hardware driver is not an OO concept.

Quoted text here. Click to load it
I agree, but again OO is neither necessary nor desirable to achieve this.

Ian


Re: C++ in embedded systems

Quoted text here. Click to load it
[snip]
Quoted text here. Click to load it

Not my intention. I was attempting to point out that the OO methology
was more likely to have supporting tools & Software Engineers that
have been trained to use it.

Quoted text here. Click to load it

Yes you are correct -- drivers are an implementation of a design goal.
The context of my point was a response to the previous poster's
comment that for embedded systems, that the dilution of the
association of the hardware & software was undesirable (in my
paraphrasing).

Quoted text here. Click to load it

Strictly speaking it isn't OO, but I see a close association with the
total Software Development Process. Chances are, if you're into OO
then you would have probably read Fowler & Scott's  "UML Distilled".
From there, if you're into producing "quality" software then you may
have perused Rational Unified Process methodology.

Now none of these "quality" methodolgies need be exclusive used with
OO, it's just that this is where it all started for me.

Ken.

Quoted text here. Click to load it


+====================================+
I hate junk email. Please direct any
genuine email to: kenlee at hotpop.com

Re: C++ in embedded systems
snip
Quoted text here. Click to load it

Now I understand you, but the point is at best moot.  Don't get me
started on tools and Software Engineers.

Quoted text here. Click to load it

We may be going round in circles here because I think it was I who
expressed that sentiment.

Quoted text here. Click to load it

THat's fine and exactly my point.  The overall objective is to design a
system which functions and performs correctly.  OO is just one way this
can be done but it is by no means the only one nor in my view the best
one for embedded systems.


Ian


Re: C++ in embedded systems
Quoted text here. Click to load it

You might find this article interesting:

http://www.codeproject.com/gen/design/theWrongObject.asp

Essentially, it's saying that OOD is good... but you need to be very
experienced before you really see the benefit. I tend to agree with that.

You might also find this article interesting (taken from a reply to a
similar C++/embedded thread in comp.lang.c++):

http://www.objectmentor.com/resources/articles/WhyAreYouStillUsingC.pdf


Tim



Re: C++ in embedded systems
Quoted text here. Click to load it

Coundn't get this url to work.  Can you confirm it is correct?

Quoted text here. Click to load it


Re: C++ in embedded systems
Quoted text here. Click to load it

Yes, it is correct. Unfortunately the entire CodeProject web-site seems to
have gone breast-up. Do try later; it's an interesting article: "A Look At
What's Wrong With Objects "

Quoted text here. Click to load it



Re: C++ in embedded systems

Quoted text here. Click to load it
It's working now.  Fascinating stuff.  Just proves that a design is
still only as good as the people doing it.

Ian


Re: C++ in embedded systems
Quoted text here. Click to load it

That was probably me. I avoid C++, but I embrace OO and code in C/assembler.

In response to other posts in this thread - it's not OO that's the problem,
it's *bad* OO that's the problem. To be specific, OO is really nothing more
than good decomposition. I tend to think of it as "structured data" - I
don't see it being any more/less difficult/controversial than structured
programming (and I remember when my colleagues thought that was a passing
fad too, and swore by their GOTOs). It's just a question of breaking data
and code down into "objects" in the sense of good modularity. Exactly how
doesn't matter, but bad decomposition is always bad - OO or not.

Re C++: my own (again controversial) view is that C++ is a *terrible*
implementation of a good idea and tends to lead to poor OO and poor code. My
criticisms of C++ include:
  - use of the heap (heaps have no place in embedded systems, since malloc
can fail at runtime)
  - operator overloading (renders code hard to read and frequently misleads)
  - poor hiding of private elements (since they're in the header)
  - the difficulty in reading code without having every single header file
open
  - too much emphasis on runtime (late binding, name mangling etc etc)
  - exception-handling (no substitute for good error handling)
  - bloat in general

Regardless of the buzzwords, *good* code means that every module has a
defined public interface that tells you everything you need to know abouit
that module. If it's well done, you shouldn't need to look under the hood -
just use the interface. My view (to repeat myself) is that software
engineers often fail in this - and OO makes this all the more painful, but
it's essentially poor design whichever way you look at it. (If you need an
example of bad OO, consider MFC.)

The bit that matters:
  - OO saves me time. It renders my projects clearer and more maintainable.
  - C++ does not seem to save programmers time. I've seen teams struggle
with comprehension before the first release, let alone later.
  - My own stated objectives are clarity and hence maintainability. I don't
really care too much how this is achieved, so long as it *is* achieved.

Steve
http://www.sfdesign.co.uk
http://www.fivetrees.com



Re: C++ in embedded systems

:   - use of the heap (heaps have no place in embedded systems, since malloc
: can fail at runtime)

You can do all allocation statically in C++ as you would do in C, too.
Nothing is forcing you to use the heap in C++.

Think of C++ as 'OO C, forgetting STL and templates' - execpt you have
inheritance and the possibility of creating interfaces
('pure virtual' functions) nor need to pass 'this' pointer around
explicitely.

Both decrease the error possibility and add safety by eliminating the
need for a switch-case -statement (compiler does it compile time).

Inheritance is a compile time mechanism in C++ and doesn't cost
anything runtime, a virtual function call is one memory access
more (a lookup to vtable).

:   - operator overloading (renders code hard to read and frequently misleads)

This is true. Operator overloading is very often used in a wrong way,
but somewhere it fits.

:   - exception-handling (no substitute for good error handling)

Isn't exception handling error handling also?

:   - bloat in general

Care to emphasise? Using class or function templates can cause this, but
if you implemented the same functionality not using templates, and
created a version of a template function for all data types you need,
you get the same amount of code execpt that the C++ compiler does it for
you.

Re: C++ in embedded systems

Quoted text here. Click to load it

IMO, the language itself is just way to big and complex. Almost
nobody understands it well enough to use it effectively and
safely.  Comapre the C++ language spec with the Modula-3 spec,
or the Scheme spec (through in CLOOPS even), or the Smalltalk
spec.  

--
Grant Edwards                   grante             Yow!  Darling, my ELBOW
                                  at               is FLYING over FRANKFURT,
We've slightly trimmed the long signature. Click to see the full one.
Re: C++ in embedded systems
snipped-for-privacy@visi.com (Grant Edwards) wrote in message

Quoted text here. Click to load it

Or even the Ada spec...  ;-)

Re: C++ in embedded systems
Quoted text here. Click to load it

Yup -- I've even got a copy of that somewhere...

--
Grant Edwards                   grante             Yow!  Where's th' DAFFY
                                  at               DUCK EXHIBIT??
We've slightly trimmed the long signature. Click to see the full one.
Re: C++ in embedded systems

Quoted text here. Click to load it

I disagree.  I think that in embedded work, it's import for the
programmer to have a thorough understanding the the language
and what the compile is going to do.  From what I've seen,
that's possible with C, Modula-[23], Ada, and various other
languages.  It doesn't seem to be possible with C++.  The
_language_ itself is too complex and baroque to understand
completely enough for it to be used effectively in some
embedded environments.

--
Grant Edwards                   grante             Yow!  I know how to get the
                                  at               hostesses released! Give
We've slightly trimmed the long signature. Click to see the full one.
Re: C++ in embedded systems
On 14 Aug 2003 17:34:12 GMT, snipped-for-privacy@visi.com (Grant Edwards)

Quoted text here. Click to load it

Agreed.  I've spent some time getting familiar with the code
generated by C+ compilers for various constructs.  Mostly, this
has been with compilers designed for the x86 used in PCs, but
it's not limited to that.  There's also a few articles and a
book or two around which discuss the implementation details,
too.

Templates instantiated for a data type often cause functions I
don't actually use to be generated __and__ linked, for example.
I know that if everything is done which should be done and the
linker and compiler work together flawlessly, it's possible to
correctly eliminate almost all of this.  But what chance is
there that an embedded compiler (other than GNU?) tool chain
will get the time put in, here?  Even in the case of x86
compilers, where there is a great deal of effort and time spent
in development, they fail to get this done well enough for many
smaller embedded systems.

For these smaller systems, too, partial template specialization
is almost a requirement.  Who provides that for embedded work?

So, don't use templates, you say??  Okay.  Then what about
exception handling?

The compiler still generates defensive code even when I don't
use any syntax in my code for them; due, in part, to the
separate compilation requirement, if nothing else.

So, don't use exceptions, right??  But a properly functioning
C++ compiler must generate correct code for source code unit A
when it has absolutely no idea what kind of exception handling
may be required in source code unit B, compiled separately and
perhaps at a very different time.  And linkers I'm aware of
don't cover this issue, either.

Take this sequence of code, found as part of some function in
some arbitrary source code file:

       .
       .
       foo ();
       String s;
       foo ();
       .
       .

For purposes here, let's assume this fragment occurs as part of
a function sitting in a module that nowhere in it uses or
handles expections in any way.  Let's say, in fact, that this
module *could* be compiled by a vanilla C compiler, except for
the use of class objects illustrated above.  That's the only
aspect of C++ that's in use, here.  In fact, let's say that this
use of a string is the *only* non-C aspect in the explicit code.
Okay?

Now assume that foo() is an external procedure and that the
compiler has a declaration for, but does not know its definition
at this point in time.

The C++ compiler sees the first call to foo() and can just allow
a normal stack unwind to occur if foo() throws an exception.  In
other words, no extra code is needed at this point to support
the unwind process.  So this call to foo() requires nothing
special, no C++ "fluff," so to speak.  The compiler generates
nothing it wouldn't generate if it were a C compiler.

But once String s has been created, the C++ compiler knows that
it must be properly destroyed before an unwind can be allowed if
an exception occurs later on.  It doesn't matter whether the
function in this module uses exceptions.  Or even if the module
itself ever does.  It only matters that the exception *might*
happen, even outside the module, because foo() is an external
function and the C compiler cannot verify that foo() doesn't
call some other function which *can* throw an exception.

So the second call to foo() is semantically different from the
first.  If the 2nd call to foo() throws an exception (which it
may or may not) in foo(), the compiler must have placed code
designed to handle the destruction of String s before letting
the usual unwind occur.  This means that the first foo() gets
coded up differently than the second foo() call.

Now, the above is a case where a common, garden variety embedded
programmer would imagine that the two calls to foo() would take
the same time, require the same resources, and otherwise be the
same.

How about this simple bit of C++ code:

  struct T { ~T() { /* assume some non-trivial code here */ } };
  extern void foreign ();
  void example ();
  void example () { T s; foreign (); }

This specifies a destructor for T which must be called in case
of exceptions.  example() calls a foreign function which may
generate exceptions.  Since the compiler of this code has no way
to be certain that foreign() cannot throw an exception, it must
assume that it might.  Accordingly, the compiler needs to
generate the necessary code to handle the destruction call to
obliterate the object T if such an exception does occur in
foreign().

In other words, exception handling quite often comes at a price,
even in functions that the programmer knows cannot generate
exceptions.  And this is especially true because of the
semantics of classes, and their associated destructors and
constructors.

Unlike C's malloc, C++'s new is supposed to use exceptions to
signal when it cannot perform raw memory allocation.  In
addition, so will <dynamic_cast>.  (See Stroustrup's 3rd ed.,
The C++ Programming Language, pages 384 and 385 for the standard
exceptions in C++.)  Many compilers allow this "proper behavior"
to be disabled, of course.  But if you stay true to C++ form,
you will incur some overhead due to properly formed exception
handling prologues and epilogues in the generated code, even
when the exceptions actually do not take place and even when the
function being compiled doesn't actually have any exception
handling blocks in it.

Stroustrup has publically lamented this practical reality.

So, don't use classes with constructors and destructors?

Right.

Some more thoughts,

When a C++ function returns an object an unnamed compiler
temporary is created and destroyed.  Some C++ compilers can
provide efficient code if an object constructor is used in the
return statement, instead of a local object, reducing the
construction and destruction needs by one object.  But not every
compiler does this and many C++ programmers aren't even aware of
this "return value optimization."  I know that they should be
and that when you use C++ features, you need to know what they
cost you.  But it remains one of those common risks which C
exposes (because it doesn't support these semantics directly)
and which C++ can too easily hide.

Providing an object constructor with a single parameter type may
permit the C++ compiler to find a conversion path between two
types in completely unexpected ways to the programmer.  This
kind of "smart" behavior isn't part of C.  C++ compilers can
automatically generate constructors, destructors, copy
constructors, and assignment operators for you, with unintended
results.

A catch clause specifying a base type will "slice" a thrown
derived object, because the thrown object is copied using the
catch clause's "static type" and not the object's "dynamic
type."  A not uncommon source of exception misery (assuming you
can afford exceptions in your embedded code, in the first case.)

Passing arrays of derived objects to a function accepting arrays
of base objects, rarely generate compiler warnings but almost
always yields incorrect behavior.

Since C++ doesn't invoke the destructor of partially constructed
objects when an exception occurs in the object constructor,
handling exceptions in constructors usually mandates "smart
pointers" in order to guarantee that constructed fragments in
the constructor are properly destroyed if an exception does
occur there.  (See Stroustrup, page 367 and 368.)  This is a
common issue in writing good classes in C++, but of course
avoided in C since C doesn't have the semantics of construction
and destruction built in.  Writing proper code to handle
the construction of subobjects within an object means writing
code that must cope with this unique semantic issue in C++; in
other words "writing around" C++ semantic behaviors.

C++ copies objects passed to object parameters.  For example, in
the following fragments, the call "rA(x);" may cause the C++
compiler to invoke a constructor for the parameter p, in order
to then call the copy constructor to transfer object x to
parameter p, then another constructor for the return object (an
unnamed temporary) of function rA, which of course is copied
from parameter p.  Worse, if class A has its own objects which
need construction, this can telescope disasterously.  (A C
programmer would avoid most of this garbage, hand optimizing
since C programmers don't have such handy syntax and have to
express all the details one at a time.)

    class A ;
    A rA (A p) { return p; }
    // .....
    { A x; rA(x); }

longjmp doesn't have a portable behavior in C++.  (Some C
programmers use this as a kind of "exception" mechanism.)  Some
C++ compilers will actually attempt to set things up to clean up
when the longjmp is taken, but that behavior isn't portable in
C++.  If the compiler does clean up constructed objects, it's
non-portable.  If the compiler doesn't clean them up, then the
objects aren't destructed if the code leaves the scope of the
constructed objects as a result of the longjmp and the behavior
is invalid.  (If use of longjmp in foo() doesn't leave a scope,
then the behavior may be fine.)  This isn't too often used by C
embedded programmers and they should make themselves aware of
these issues before using them.  So don't use longjump()?

How many people using C++ know exactly how the vtable mechanism
works?  In the face of multiple inheritance?  With virtual base
objects?  How many people how a C++ compiler supports dynamic
casts or know what causes the C++ compiler to generate (or not
generate) support for it?  What mechanism does a C++ compiler
use for exception handling?  What does it cost?  Where?  If one
stays away from a mechanism, how much of it is still present?

And all the above is just from the cuff -- there is much more.

Jon


Re: C++ in embedded systems
Quoted text here. Click to load it
<lots of good stuff snipped>

Excellent post, Jon. Enlightening.

Steve
http://www.sfdesign.co.uk
http://www.fivetrees.com



Re: C++ in embedded systems
On Thu, 14 Aug 2003 18:52:45 GMT, Jonathan Kirwan


Quoted text here. Click to load it

The calls to foo() in the above *are* sematically the same because the
both calls occur within the same scope.  The declaration of the string
object between them doesn't matter.  That the calls may be
implementationally different is a matter to discuss with the compiler
vendor.

C++ syntax allows the variable declaration to be at the point of first
use but the variable may be constructed at any time after the
enclosing scope is opened and prior to its first use.  Your particular
compiler (indeed most compilers) may defer constructing the variable
until its first use but that behavior is not guaranteed.

The generated destructor call in your example has nothing to do with
exceptions per se - the compiler is required to ensure destruction of
automatic objects when they go out of scope.  The possibilty of a
non-local return through foo() changes the how but not the why.  


Quoted text here. Click to load it

Wrong ... "s" is an automatic object.  Even though you explicitly
declared type T to be a struct, the addition of destructor made it an
class. See above.

The automagic promotion of structs to classes is one thing about C++
that I violently disagree with.  All kinds of warnings should go off
when code like the above is written.



Quoted text here. Click to load it

Not gonna argue this one except to say it is not unique to memory
allocation ... the semantics of objects and non-local control
transfers in the same language has certain costs.




Quoted text here. Click to load it

This is no different from a C compiler promoting function parameters
and you aren't complaining about that.

If you have a class A which can be cast to a B and a class C which can
be constructed from a B, then C can be constructed from A.  So what?
This is no different from any other composition of function calls and
it requires the classes to be explicitly written to allow it.

I would call this a case of "know thy program".



Quoted text here. Click to load it

The default assignment operator is usually a bitwise copy (just like
for a struct).   I'm not aware of any compilers that do anything more
intelligent with it.

Personally, I would prefer that the assignment operator not be
generated, but it is and programmers have to be aware of it.



Quoted text here. Click to load it

You've explicitly declared that the base type is all you are prepared
to handle.  Are you complaining that the copy is inefficient or that
you might really want the derived object - perhaps to rethrow it?

If you want the derived object to rethrow, you should catch a pointer
to the base class and dynamically allocate the thrown object.
Naturally, that has its own problems 8-)



Quoted text here. Click to load it

No argument ... this is just something to be aware of.  Turning on
automatic promotion warnings will usually catch this ... as well as
every other nit picking implicit cast you failed to code explicitly.



Quoted text here. Click to load it

There is a school which teaches that constructors should never fail
and that initialization code which can fail should be in an explicit
function.  

Destructors, similarly,  should not be relied on to release any
resources other than memory.  

A lot of C++ example code is badly written.



Quoted text here. Click to load it

setjmp/longjmp are included in the C++ standard library because C++
compilers are also C compilers.  They are definately NOT to be used
when writing C++ code.  

The documentation accompanying the compiler tells you this - along
with telling you that malloc and free are not to be used with objects
and that decorated function names are not compatible with legacy C
code and that ... etc., ad nauseam.

Compilers are not door knobs.  The programmer has to read the
documentation.



Quoted text here. Click to load it

As a guess ... about the same percentage as C programmers who know how
the C runtime works.  Very few programmers nowadays know anything of
how their chosen language accomplishes its magic.  Such things are not
taught (except to CS's studying language implementation) and are
simply not an issue to most application programming.


Quoted text here. Click to load it

C++ was designed with the idea that the program should not have to pay
for features it does not use.  The degree to which compilers achieve
this ideal is open for debate.  Also, most of the overhead is related
to the use of objects rather than exceptions.  


Exception handling effects a non-local transfer of control, just as
longjmp does, but adds controlled destruction of any automatic objects
created between the throw and catch points.  The "unwinding" is of
variable scopes rather than functions per se.  The mechanism used is
compiler dependent, but a simple implementation might look like the
following:

Functions all look something like:

    foo ( jmp_buf chain,  ...   )
    {
        jmp_buf link;
        int error;
        
        error = setjmp(link);
        if ( error == 0 )
        {
            /* do code for foo(), pass link to called functions */
            bar( link,  ... );
        }
        
        {
            /* clean up foo() objects  */
        }

        if  ( error != 0 )
        {
            /* rethrow to next level */
            longjmp( chain, error );
        }
    }

and a try-catch call itself looks something like:

    :
    {    
        jmp_buf trychain;
        int error;

        error = setjmp(trychain);
        if ( error == 0 )  
        {
            foo( trychain, ... ) /* try */
        }    
        else
        if ( error == ... )
        {
            /* caught */
        }
        else
        {
            /* uncaught exception */
        }
    }
    :    

Obviously this simple example doesn't deal with object returns nor
does it capture all the nuances, but demonstrates how easily most of
the job can be done.   Presumably the compiler would eliminate the
cleanup code and (hopefully also) the linkage code for functions that
have no automatic objects.  Such a function needs only pass the
existing chain into any to functions it calls.


Function calls can be statically bound at compile time for classes
that don't use virtual functions and also for virtual function calls
in cases where the compiler is certain of the object's type.
Similarly calls to overloaded functions can be statically determined
at compile time if their parameter types are known.

A simple class costs little more than a C struct: a pointer field in
each object and a generated destuctor call where an automatic object
goes out of scope.  The equivalent of constructors and assignment
operators would have been open coded in C anyway.  Default destructors
do nothing and are eliminated as dead code - anything else would have
been coded anyway.

Run Time Typing adds an extra field in the vtable of each class and
assigns a place-in-heirarchy code to each class.  For single
inheritence the runtime implementation of dynamic cast is just a
pointer dereference to get the class code, a comparison to the
statically known target class code, an offset added to the object
address and an assignment to set the result.  Maybe a half dozen
instructions.

Multiple inheritence can make all runtime type decisions much more
difficult and requires more intrinsic support code.  I'm not greatly
familiar with techniques for handling it so I won't comment further.
But since it isn't necessary for most (all?) programs, most compilers
can disable support for it.



It all comes down to knowing your compiler.  This is true for any
language.  I'll grant that C++ has more to be mindful of than C, but
it does more.   Debating whether a certain mechanism should be hidden
or not is pointless - the gods have decreed that most of C++'s
advanced mechanisms be hidden.  Live with it or don't use it.  

I'm all for making people aware of the pitfalls and tradeoffs of a
particular language (or implementation) but telling people to avoid
using some language because you don't like how it works is a
disservice to everyone.  (Jonathan, you didn't really do this and I'm
not talking specifically to you, but rather to everyone here.  These
language threads can be useful only where there is thoughtful
discussion.  The "mine is better than yours" posts contribute nothing
but noise.)

George

Re: C++ in embedded systems


[8<8<8<]
Quoted text here. Click to load it

Could you be more specific about which compilers you have observed what.

Quoted text here. Click to load it

The book
   Inside the C++ Object Model
   Stanley B. Lippman
   ISBN 0-201-83454-5
describes in details what (pseduo C) code a C++ compiler might generate for
various constructs.
Although the book is prestandard (1996) it contains a lot of usefull
information.

Also have a look at
   Technical Report on C++ Performance
   http://std.dkuug.dk/JTC1/SC22/WG21/docs/papers/2002/n1396.pdf
which is written by the C++ Standard committee
<quote>
  * to give the reader a model of time and space overheads implied by use of
various C++ language and library features,
  * to debunk widespread myths about performance problems,
  * to present techniques for use of C++ in applications where performance
matters, and
  * to present techniques for implementing C++ Standard language and
Standard library facilities to yield efficient code
</quote>

Quoted text here. Click to load it

Could you give a specific example ?

I think that we need to be carefull about what we are talking about here.
We can be talking about:
  1. Function templates
  2. Non-virtual member functions of class templates
  3. Virtual member functions of class templates

1.
Function templates like
  template <T>  void foo(const T* t);
are only implicit instantiated if they are used.
Thus for Standard compliant C++ compilers, it's a non issue.
It's even hard to image how it could be an issue for non compliant
compilers.

2.
Non-virtual member functions of class templates like
  template <T>
  class bar
  {
  void  foo(const T* t);
  };
are only implicit instantiated if they are used.
In fact the compiler are not even _allowed_ to instantiate the non-virtual
member functions if they are not used.
It is not requiered that the member functions is compileable at all, if it
is not used like shown below
<C++ code>
#include <iostream>

template <typename T>
class bar
{
public:
   void   foo(const T* t);
};

template <typename T>
void bar<T>::foo(const T* t)
{
   const char* c = t;
   while (c && *c)
      std::cout << *c++;
   std::cout << std::endl;
}


int main()
{
   bar<char>   bc;         // valid
   bc.foo("hello world");  // valid
   bar<float>  bf;         // valid
   float       f = 0;
   bf.foo(&f);                // error bar<float>::foo not compileable
}
</C++ code>

Thus for Standard compliant C++ compilers it's a non issue.
Non compliant compilers might instantiate every member function for class
template for every type that class template is used.
However it is long time ago (mid 90's) I saw that the last time.

See 14.7.1-9 in the C++ Standard for the details.

3.
Virtual member functions of class templates are likely (allowed, but not
required) to be instantiated and linked, even if they are not called.
The same is the case for virtual functions for classes that are not class
templates.
Thus it's an attribute of the virtual function mechanism and not the
template mechanism.

Quoted text here. Click to load it

Pretty good I would expect.
Many embedded C++ compilers (like Green Hills, Microtec, DIAB) are based on
the EDG front-end (http://www.edg.com/customers.html ), which, for at least
the last decade, has only implicit instantiated member functions of class
templates which are required to exist.

If you are using a C++ compiler, which does not have the behaviour as
required by the C++ Standard and commonly implimented by a lot of compilers
you should either complain to the vendor or change compiler vendor.

Quoted text here. Click to load it

Which x86 compilers are you refering to ?
On the MS-Windows platform the 2 most used compilers are probably Microsoft
C++ and Borland C++.
Since approximate the middle of the 90's those compilers have not
instantiated not used, non-virtual member functions.
And note that these compilers where behind the state-of-art in this respect
at that time.

Quoted text here. Click to load it

A lot I would expect.
We have been using partial template specialization for embedded projects for
more than 5 years using a compiler based on a rather older EDG frontend.

Quoted text here. Click to load it

No.
The combination of templates and inline functions is a very efficient way of
writing high level, high performance code - also for embedded systems.
I've been doing that for many years.

There are many libraries which uses compile-time polymophism (as opposed to
run-time polymophism like virtual functions) to provide both a higher
abstaction level and performance at the same level as C or better.
The C++ Standard library function std::sort is a classic, simple example but
there are many others.

Quoted text here. Click to load it

No - understand the consequences of using exceptions, before deciding.
Both in terms of performance and in terms of programming style.
You should know how to write exception safe code before using exceptions in
C++.
It is well understood and well described and has been so for years.
See
  The C++ Programming Language, Special Edition
  Bjarne Stroustrup
  ISBN 0-201-70073-5
appendix E (that appendix can be downloaded from Bjarne Stroustrup's
homepage http://www.research.att.com/~bs /).
And
  Exceptional C++
  Herb Sutter
  ISBN 0-201-61562-2
and
  More Exceptional C++
  Herb Sutter
  ISBN 0-201-70434-X

Exception comes at a price - even if no exceptions are ever thrown.
But so does every error handling strategy.
Even ignoring error handling comes at at price :-)

Quoted text here. Click to load it

It is possible to compare error handling strategies with respect to various
parameters like
  * code size
  * execution performance when no error happens
  * execution performance when an error happens
  * amount of effort requiered by the programmer
  * how error prone are the error handling strategies
However it is not resonable to compare a program, which handles error
conditions correct with a program which does not handle error conditions.

Let's be a bit more concrete and compare an example which rely on
constructor/destructor and exeptions with an example which rely on error
codes and explicit initialization and clean up and error codes.
Let's take an example which opens 3 files and processes the files - both
examples are C++ but the style is different:
<C++ code>
void foo_throw();
int foo_code();

void bar_1(const char* f1, const char* f2, const char* f3)
{
   ifstream    is1(f1);
   if(!is1)
      throw runtime_error("unable to open first file");
   foo_throw();   // might throw

   ifstream    is2(f2);
   if(!is2)
      throw runtime_error("unable to open second file");
   foo_throw();   // might throw

   ifstream    is3(f3);
   if(!is3)
      throw runtime_error("unable to open third file");
   foo_throw();
}

int bar_2(const char* f1, const char* f2, const char* f3)
{
   FILE*    is1 = fopen(f1, "rt");
   if(!is1) return 1;
   int      result = foo_code();
   if(!result)    {
      fclose(is1);
      return result;
   }

   FILE*    is2 = fopen(f2, "rt");
   if(!is2) {
      fclose(is1);
      return 1;
   }
   result = foo_code();
   if(!result)   {
      fclose(is2);
      fclose(is1);
      return result;
   }

   FILE*    is3 = fopen(f3, "rt");
   if(!is3) {
      fclose(is2);
      fclose(is1);
      return 1;
   }
   result = foo_code();
   fclose(is3);
   fclose(is2);
   fclose(is1);
   return result;
}
</C++ code>

In my opinion "bar_1" is far simpler than "bar_2".
The use of constructors/destructors _garanties_ that there can't be any
resource leaks.
The use of exceptions means that error handling doesn't clutter the normal
flow and there is no way that we can forget to test for an error.

[8<8<8<]
Quoted text here. Click to load it

It depends ...
What's the declaration of "foo" ?
   1.  void foo();
   2.  void foo() throw ();
   3.  extern "C" void foo();

The first declaration says that "foo" might throw anything, thus the
compiler has to deal with that in some way.
For the second and third declaration the compiler knows that there is no way
an exception can propagate out of the function, thus it doesn't have to
generate anything extra.

[8<8<8<]
Quoted text here. Click to load it

I don't quite understand what you mean.

Of course the program flow is different if an error occurs, if the program
is supposed to behave correctly.
It doesn't matter whether you use constructor/destructor or not.
It doesn't matter whether you use exceptions or not.

Quoted text here. Click to load it

No.
"foo" gets coded exactly the same way - it's the very same function that is
called.

With modern high quality compilers the call to "foo" is not coded
differently for the first and the second call.
Neither calls are augmented with error handling code - there is only the
"call" assembler instruction. The exception handling code is only executed
if an exception is actually thrown - there isn't even a test to check if an
excetion is thrown.
I have verified that with Intel C++ V7.1 and Microsoft Visual C++ .NET 2003
on MS-Windows and g++ V3.2 on Linux.
The compilers for MS-Windows are of course are not specificly for the
embedded market, but there is no reason why embedded compilers shouldn't be
able to do the same thing.
The Intel compiler is based on the EDG front-end - just like several C++
compilers for the embedded market. It is likely that the exception handling
code is handled by the front-end.

The program behaves differently depending on whether an error condition
occurred.
But that's not a matter of exceptions/not exception - it's a matter of
program correctness.

Quoted text here. Click to load it

Who would expect that the the two calls take the same time and require the
same resources if the first call succeed and the second call fails ?

If both calls to "foo" succeed it is fully possible and not unlikely that
the two calls would take the same time and require the same resources.
In fact they do with the compilers mentioned above.

See "Technical Report on C++ Performance" chapter 2.4 for at detailed
description the "code" approach and the "table" approach to exception
handling.

If there is no way that "foo" can fail, you can tell the C++ compiler that
in the function declaration with an empty throw specification.

Quoted text here. Click to load it

Again, if there is no way "foreign" can fail, we can specify that by:
void foreign() throw();

Quoted text here. Click to load it

Sure.
But T::~T is going to be executed in any case, simply because "s" has been
constructed.

Quoted text here. Click to load it

Why not tell the compiler what he/she knows with an empty throw
specification ?

Quoted text here. Click to load it

Constructors/destructors are a huge help.
It makes it far easier to write correct programs.


Quoted text here. Click to load it

Yes.
That means that
  * You don't have to clutter your normal program flow with error handling
code
  * There is no way you can forget to handle an out-of-memory situation

Quoted text here. Click to load it

For references - not for pointers.

Quoted text here. Click to load it

It doesn't have to be that way.
See "Technical Report on C++ Performance".

If no error occurs you will have error handling code, which is never
executed.
The same is the case if functions return error code:
   if(!foo())  {
     // error handling code goes here
     return;
   }


[8<8<8<]
Quoted text here. Click to load it

No.
They make it far easier to establish invarians in constructor and ensure
correct cleanup in destructors.

[8<8<8<]
Quoted text here. Click to load it

You can you the "explicit" keyword in C++ to prevent implicit conversions.

[8<8<8<]
Quoted text here. Click to load it

You can most definitely write code in C++ which behaves unexpected.
But why wouldn't you always catch exception objects by const reference ?
  try  {
     // ...
  }
  catch(const  std::exception& x) {
    // ...
  }

[8<8<8<]
Quoted text here. Click to load it

That not very accurate.
It is easiest to use smart pointers if the member sub-object are dynamic
allocated.
You can also write try/catch clauses in the initialiser list.
Often member object are simply members, and there is _nothing_ to worry
about:

class unconstructable
{
public:
    unconstructable()
      {   throw 1;  }
};

class foo
{
public:
   foo(const char* text_1, const char* text_2) :
    s1(text_1), s2(text_2) {}
private:
   std::string    s1;
   std::string    s2;
   unconstructable halt;
};

In this example "s1" and "s2" will (normally) be fully constructed before it
is attempted to construct "halt" which will always fail.
There is _no_ need for smart pointers or manual intervention to ensure that
"s1" and "s2" are properly destructed and resources are released.

Quoted text here. Click to load it

Often you dont' have to write anything - as shown above.
It's a simple matter of using an effecient coding style.

When writing C++ you must deal with C++ semantics.
Does that suprise you ?

[8<8<8<]
Quoted text here. Click to load it

Of course you can write sub-optimal programs in C++ - the language doesn't
prevent that.
But why should we ?
You can simply write:
  A  rA(const A& p) { return p;  }
to prevent the compiler from creating a copy when parsing the argument p to
the functions.
A lot of compilers will do return value optimization.

[8<8<8<]
Quoted text here. Click to load it

It sounds like FUD to me.
A lot of people that I know has at least an understanding of the principles
of what code the compiler might generate.
Of course there are a lot more people who doesn't. My mother doesn't know -
but she doesn't program embedded devices in C++, so it doesn't matter.
It doesn't take a genious to understand the principles of what code the
compiler might generate.
It does however take education - but the sources are available.

Kind regards

Mogens Hansen








Re: C++ in embedded systems

Quoted text here. Click to load it

[8<8<8<]
Quoted text here. Click to load it
for

Sorry.
I was thinking of full member specialization.

But still I would expect several C++ compilers for the embedded market to
support partial template specialization.

Kind regards

Mogens Hansen



Site Timeline