C++ in embedded systems

Or even the Ada spec... ;-)

Reply to
Robert Wessel
Loading thread data ...

I think I have met 3 of them.

Reply to
Richard Henry

You might find this article interesting:

formatting link

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++):

formatting link

Tim

Reply to
Tim Clacy

As a C++ developer for embedded systems with some twenty years of toil behind me, I have nothing but respect for Forth developers. I envy the fact that they can develop interactively and be running code on new hardware in minutes. We have many more obstilces in our way (compiler, linker, debug target, download agent, debug client; all of these have to work before anything works). We work far from the hardware; forth developers are in contact with it.

Reply to
Tim Clacy

You are entitled to your opinion as am I.

Ian

Reply to
Ian Bell

Including that one?

Ian

Reply to
Ian Bell

CBFalconer wrote: snip

This is the crux of the matter and the key word is gainfully. My view is that the OO metaphor/paradigm (PYO buzzword) does not of itself provide any gains in the development of embedded systems. It is not proven that it is better in this context than other design methodology.

Ian

Reply to
Ian Bell

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.

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

I agree, but again OO is neither necessary nor desirable to achieve this.

Ian

Reply to
Ian Bell

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

Reply to
Ian Bell

Tee hee. But if it was true, I would have been out of business long ago. Modern Forth compilers produce good code, and where code density matters, Forth still produces applications that are often half the size of C applications. And yes, I do have numbers to back this up for a payment terminal (8051) and an embedded web server (68xxx, H8S and ARM).

Very true. I would also add that with the exception of web weenies, very few junior programmers have used any languages other than C/C++ and perhaps Java. This in distinct comparison from 15 years ago, when most programmers in any field had actually used a much wider range of languages and had the experience to be able to select an appropriate language for a job. If you have never used interactive languages, you will never be able to appreciate what they have to offer. Remember Smalltalk, Eiffel, BASIC ... Any community misses out if its focus is on a range of culturally similar languages, programming or spoken.

Apart from OO (the current silver bullet), there is little to separate any of the batch compiled languages such as C, C++, Pascal and so on. At least Modula 2 dealt with the dependency issues.

It is August and the silly season is upon us :-)

Stephen

-- Stephen Pelc, snipped-for-privacy@mpeltd.demon.co.uk MicroProcessor Engineering Ltd - More Real, Less Time

133 Hill Lane, Southampton SO15 5AF, England tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691 web:
formatting link
- free VFX Forth downloads
Reply to
Stephen Pelc

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 "

Reply to
Tim Clacy

Yes. No absolute statements are true.

:-)

-- Chuck F ( snipped-for-privacy@yahoo.com) ( snipped-for-privacy@worldnet.att.net) Available for consulting/temporary embedded and systems. USE worldnet address!

Reply to
CBFalconer

We seem to have rediscovered recursion.

Ian

Reply to
Ian Bell

Hehe. No, it just means that absolute claims are meaningless, not false or true.

Jon

Reply to
Jonathan Kirwan

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

--
Grant Edwards                   grante             Yow!  Where's th' DAFFY
                                  at               DUCK EXHIBIT??
                               visi.com
Reply to
Grant Edwards

EC++ might be correct to leave out RTTI and C++ EH, but disagree with some of the other decisions. The assumption seems to be that embedded developers are not as competent as desktop developers and that embedded software is much simpler (I'm thinking of mutable ["it might confuse them"] and namespace [embedded software isn't big enough]).

Reply to
Tim

In most "embedded designs," I either use assembler or C++ and assembler. In particular, GCC's in-line assembler is extremely powerful (once you get the hang of it). In my experience, I have not run across any situation where I could find a performance advantage between C and C++ -- if the processor has the architecture and performance headroom to support C it will also support C++.

The C++ advantages and drawbacks are like everything else in a tight design -- you have to be sure you can afford the convenience. This judgment comes from experience, and it may be that most C++ programmers work in the user application space where efficient coding often does not bring performance advantage to the overall enterprise system. After all, if you are shoveling megabytes XML across your 1.544 mb/s internet connection, who gives a flip whether you tighten up some inner loop with hand done assembly language? But for real embedded work, where you are trying to shoehorn the product into the cheapest microprocessor with the fewest resources, here are some advantages over C that C++ brings to the table:

  1. Classes. It's very nice in embedded systems to organize pieces of hardware into objects. For instance, in a very tight AVR project where I am counting cycles, I have a class that wraps the SPI port with an interrupt handler, several command methods, a foreground routine, and logic code to generate and decode packets sent to/from an FPGA. I did it this way to make it simple for me and the FPGA designer to stay on the same page, even editing each others code. But after we were done, I abandoned plans to tighten up its performance. It is better than I could do with straight assembler, and it's more than fast enough.

  1. overridden new/delete operators. I can override new and delete operators, swapping in a custom memory manager to replace "stock" dynamic memory (malloc/free) support. In designs where I need dynamic memory, but can't afford a heap, this is a very nice way to get screaming performance in a manner transparent to the application.

In my experience, here are some C++ features that may or may not be helpful:

  1. Multiple inheritance. This adds a lot of code and overhead to function calls as your "this" pointer is transparently moved about. In some cases, I have used sloppy multiple inheritance to get a prototype running, but after profiling, gone back and replaced some of it with explicit code. However, when simple member functions can be implemented inline, then the optimizer usually completely removes the overhead.

  1. Virtual Functions. If you're playing tricks in C with function pointers to add abstraction, sometimes C++ virtual base classes can do it better with less opportunity for error. This is not always the case, but in my experience it sometimes works this way.

  2. Overloaded operators. I don't like this feature at all. It doesn't really add overhead, but it's confusing to me. A lot of people love it though. Maybe it's a matter of taste.

Even very good people look at me askance when I say I uses C++ for embedded applications. However, it works for me. I am able to bring projects in on time and they work correctly. It's tough times, and I am able to stay very competitive with embedded C++.

Reply to
Ian McBride

eC++ dumbs down the language under the pretense that embedded programmers are too stupid to properly use the deleted features. In reality, I think eC++ is a cop-out by compiler vendors who can't keep up with GNU. They want to change the rules for the sake of their marketing departments.

Reply to
Ian McBride

: However there are the equivalent of constructors and destructors, : both for complete hash tables and for items to be stored, and : operations to be performed on them. But there is no C++ bloat (it : is written in C) and no known re-entrancy problems.

What C++ bloat?

An implementation with the same features done with C++ is likely to be smaller and faster than a C implementation, because 'void *' is not used which causes aliasing problem and prevents some optimizations. C99 has the 'restrict' keyword these days though..:

formatting link

Also inlining hashing and especially equality comparasion code in C++ implementation likely increases performance and even might reduce size. We'll see what happens..

I'm in the process of writing an implementation using interfaces and inheritance for hash- and equality comparaision function pointers instead. Comparing C implementation template-based implementation isn't probably fair, but that would make even more difference.

The difference between C++ and C implementation isn't going to be very high with a reasoable hash function since it probably dominates, but I guess the difference could be tested by a hash function that runs in a short constant time. Although in C++ implementation returning 0 as a hash value results in the compiler probably detecting it and elimination of few statements, so that's not a very good comparasion either.. I'll try to figure something out.

I'll compile your code and my code using gcc 3.3.1 cross compilers for AVR and H8. Any preference which models?

This will probably take few days since it has been a while I have been programming C++, and I have to make myself more familiar with your test code of hashlib.c so that I can verify my implementation.

Reply to
Jyrki O Saarinen

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.

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.

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.

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".

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.

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-)

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.

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.

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.

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.

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

Reply to
George Neuner

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.