validity of ... reasons for preferring C over C++

Unless they are "final"! When poking in other folks' code, I find it helps to make things final/const when you think they should be, and see where things break.

--
(Remove the obvious prefix to reply privately.) 
Gemaakt met Opera's e-mailprogramma: http://www.opera.com/mail/
Reply to
Boudewijn Dijkstra
Loading thread data ...

Ok -- I thought you meant a "pro-Ada perspective" in the sense of being biased or slanted in favour of Ada, but I misunderstood you.

This reminds me of a post on comp.lang.ada, several years ago. The poster, who was a student, was tasked with writing a seminar presentation or an essay (I forget which) comparing the relative benefits of C and Ada, and wanted to present a "balanced view" but was a bit desperate because all available reports were in Ada's favour... IIRC, the people on comp.lang.ada could not help the poster find any articles reporting comparisons favorable to C. That doesn't prove anything, of course.

--
Niklas Holsti 
Tidorum Ltd 
niklas holsti tidorum fi 
      .      @       .
Reply to
Niklas Holsti

!!! Availability of skilled C++ developers !!!

Here the biggest obstacle has been lack of available adequately skilled C++ developers. Most applicants

*really* don't know the practical realities of C++, and failed a programming test we developed (not language lawyer stuff, rather "if you don't know this you'll create code that corrupts memory" kinda things).

In the embedded space, think about how many folks with lots of C++ on their resume could answer:

1) Give some examples where C++ can help you manage low-level machine access easier than raw C. 2) In a small embedded system with no dynamic memory (other than the stack): 2a) What language features will you not be able to use? 2b) What special considerations will you expect when creating a class? 3) Give an example where using C++ a template can generate more efficient code than typical C coding.

That's off the top of my head, if I were hiring at the moment I'd give this a bit more thought...

Have you folks had the same experience? Best Regards, Dave

Reply to
Dave Nadler

I'm no C++ expert by any means, but I'll try:

Best I can say is it's easier in C++ to organize the low level operations into operations on a class encapsulating the raw machine pointers, so the compiler can catch errors more easily than if you wrote a bunch of C functions that manipulated the pointers while leaving them exposed. Operator overloading can also help abstract low level operations into higher level ones. Maybe there's a better answer than this though.

"new" and "delete" are the basic ones. A lot of the STL such as resizeable containers depend on them, as well as standard types like std::string, though those aren't language features per se. I think there may be some issue using C++ exceptions without the dynamic heap but I'm not sure.

I guess the RAII pattern can't work the same way. Is there more?

The C library qsort function requires you to pass a comparison method as a function pointer, so you incur function call overhead even if you're just comparing integers in what should be a single machine instruction. The C++ version uses a template so the comparison can be inlined.

So far I've only used C++ on big machines where I don't mind the code and memory bloat. In that situation I think it's an improvement over C. Especially with C++11 STL and using stuff like shared_ptr freely, you can program in almost the style of a garbage collected scripting language while still getting close to C speed. The error messages from STL mistakes are pretty incomprehensible, but at least the compiler has told you something is wrong at a certain line number, so with a bit of head scratching you can generally figure out the mistake.

Generally though I still think of C++ and C as dangerous legacy languages. I'm looking towards modern replacements like Rust (rust-lang.org) though those aren't really suitable for small embedded systems because of relatively complex runtimes to support the concurrency and garbage collection features.

Reply to
Paul Rubin

I guess I should add another question:

4) Under what conditions could C++ add code or memory bloat? How can it help *reduce* code and memory bloat?

Thanks for your thoughts, Best Regards, Dave

Reply to
Dave Nadler

There's lots of C++ programmers around and it's not THAT difficult a language. I don't know your situation but around here, "difficulty finding qualified help" often means "the help I need is available but I don't want to pay market rates for it".

That said, I thought your questions were interesting and I don't think I'd currently pass a serious interview to select knowledgeable C++ programmers. I've used enough C++ to get some specific tasks done and studied it a bit out of PL geekery interest, but am not extensively experienced with it.

Yes, Stroustrup keeps saying that it's perfectly well suited to the small device space, and there's not a serious reason it shouldn't be, if you program in a constrained style.

The most notorious cause of C++ object code bloat is template instantiation duplicating the same code all over the place for multiple types. Of course the same thing would happen in C if you copied functions all over, but in practice you'd tend to use some ad hoc generic scheme with void* casts, function pointer callbacks for what would be different template expansions, etc. But, I think the template generic style only bloats the binary: it actually keeps source code more concise and duplication-free (DRY principle).

I'd say there's some runtime memory bloat from using STL containers instead of specialized containers written around your specific data. I guess this can be mitigated somewhat with custom allocators though I haven't had to resort to that. There's also some memory bloat from the idiomatic C++ style of passing large objects around by value (i.e. copying them all over) though maybe that's less needed now that shared_ptr (poor man's garbage collection) is used more. There are now some hooks for actual GC as well, though I haven't used them yet.

I do think using shared_ptr or GC can simplify programs a lot, getting rid of a lot of complex and bug-prone manual storage management code, at the expense of sometimes burning more memory than is strictly needed. I like Rust's approach of combining good tracking of object ownership (allowing just-in-time reclamation without GC in a lot of cases) and GC for when you need it. I haven't yet used Rust but it's interesting.

Similarly the use of libraries like STL and Boost simplify code by supplying well-tested, fast implementations of standard data structures that in C programs end up getting implemented half-assedly over and over, differently in every program. That means smaller source code at the cost of bigger object code, since those libraries pull in code for a lot of features that you might not use.

I'm hacking on a fairly small C++ program right now that compiles to a

1.2MB binary, which seems ridiculous, like > 10x what it has any business taking. So that's an example of object code bloat. On the other hand, it's running on a 32GB machine so the 1.2MB is of no consequence.
Reply to
Paul Rubin

The only positive thing in favour of C is that the C compilers will generate code for, and run on, a lot more targets and platforms than the Ada compilers will.

As Niklas is probably aware, this is a _major_ annoyance of mine because it means you can't write portable library code in Ada which will be guaranteed to run in a large range of environments, especially embedded ones. However, you do have this assurance with C code.

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP 
Microsoft: Bringing you 1980s technology to a 21st century world
Reply to
Simon Clubley

Aren't there Ada toolchains that emit as a target language 'C'? That would at leas allow tweaking #pragmas and such to get thing shoehorned into the toolchain.

--
Les Cargill
Reply to
Les Cargill

There exists one such Ada compiler that I know of (and possibly two more I've heard rumors of). The one I know of (AdaMagic) is rather expensive, so I've never actually used it for a project. So far, it has been cheaper to spend the extra time writing C myself.

Greetings,

Jacob

--
"Facts are stubborn, but statistics are more pliable." -- Mark Twain
Reply to
Jacob Sparre Andersen

Ah. Oh well. Isn't that just the story of Ada, though? We've always been too cheap to buy it.

--
Les Cargill
Reply to
Les Cargill

+1

What helps me most is that I am able to think like a compiler, because I wrote half of one during university. Few people can do that (and it isn't a skill that embedded recruiters have on their list). So while most people can get a program correct after a while, only few can make it efficient.

Your mileage will vary, but I found it very convenient to group things such as "SPI driver" in classes. Just the fact that I can then use spi.send(data); instead of freescale_mx35_spi_send(&spi, data); would be a good argument for me.

This is a trick question, because if I don't have dynamic memory, I could simply write my own allocator.

However, I will not be able to use most off-the-shelf components. If I have a whopping 4 kilobytes of heap, I don't want a std::vector or std::string that dutifully pre-allocates me 128 elements just to make future appends faster.

Depends on the compiler, but I would try to convince the compiler NOT to generate exception-handling code. This could mean not using destructors, and maybe even constructors. It still keeps language features like templates and classes-for-encapsulation, but the experience will be very different than for a "big" app.

C++ templates can do compile-time polymorphy, that is, you use inline code where you would use function pointers in C.

For a previous project, I was writing several drivers; some already existed in a prototype version in C. Drivers needed some sort of request/result queues. The C versions implemented these queues anew, every damn time, probably with a lot of cut & paste involved. First thing I did was to add a simple Queue template class and use it everywhere. My final source code was around half the size and much more maintainable than the C version; the object code was smaller by a few percent, too.

Stefan

Reply to
Stefan Reuther

I have not seen that, c++ programs do tend to pass objects using

*references*, and then the called functions operate on them using the same syntax "as if" the reference is the object itself. But really it is just a pointer being passed (as is usual in C also).

Also c++11 has had a lot of work put into efficiently passing objects by move constructors and so forth, again it is just a pointer changing AIUI.

I have been playing with embedded c++11, just some toy programs on ARM microcontroller development boards at the moment. The minimum footprint was only a few kB, it seems absolutely fine for even the smallest projects (that I would be interested in anyway). Even using some STL and vectors and strings and so forth seemed to work without doing anything too dramatic to the code size.

--

John Devereux
Reply to
John Devereux

Um, you think like half a compiler ? ;-) Really, for C++ it is necessary to understand what happens under the hood (contrary to the definition of a good tool), but how the compiler works??

No, it is not a trick question, and that is NOT the answer...

Why does it depend on the compiler??

No, no, no... What else?

A good example of C++ helping! However, some would argue you should have implemented the queue code once using void* (and maybe item size), then wrapped it with inlines using specific names/types for safety. Hopefully you wrote a unit test for your template before using it!

Thanks for your thoughts, Best Regards, Dave

Reply to
Dave Nadler

Um, some of us know the true cost of finding, hiring, and integrating (assimilating?) the right staff. Thus we pay above market to keep them as net costs are lower...

Often that's due to including iostreams; have you looked at the map to see what has been dragged in and why?

Thanks for the thoughts, Best Regards, Dave

Reply to
Dave Nadler

RAII works just fine; the resources are not necessarily just memory. Think about disabling interrupts, grabbing an IO device, etc. ctor/dtor RAII pattern works wonderfully for these kinds of things.

Thanks! Best Regards, Dave

Reply to
Dave Nadler

Interesting point about iostreams. I haven't really looked into the bloat cause. I know that everything on 64 bit systems is big. I've idly thought about compiling the program on a 32 bit system to see if it comes out smaller. The philosophy of C++ seems to be to generate fast code above everything else. When there is a trade-off between code size and speed, it will bloat the code to get more speed. For this particular program speed is what I want, since it crunches terabytes of data for weeks at a time, so I haven't worried about the bloat.

Reply to
Paul Rubin

Different compilers will make different trade-offs. If you're compiling for a 64-bit target, the compiler probably isn't going to worry about a trifling 1.2 MB when you presumably have upwards of 4 GiB of RAM (why would you be using a 64-bit OS if you have less than that?).

Similarly, on a desktop/server OS where code is demand-paged, executable size doen't really matter; if large sections of code aren't used, they won't get paged in.

I would expect a C++ compiler for an 8-bit or 16-bit microcontroller to make somewhat different tradeoffs.

Tangentially: for iostreams, each overload of operator

Reply to
Nobody

That is at the same time a good and terrible example! Good because it illustrates the language trade-off. Bad because in a resource constrained environment, qsort can chew up more stack than is available.

A few years back I encountered a stack overflow due to an ill-advised use of qsort. I replaced it with a templatized heapsort. Later I needed to sort a

2nd type. Not enough flash space for a 2nd template instantiation, so I changed to a single concrete instance that takes a pure virtual type wrapper (which provides count, comparison and swap). The wrapper hides all access from the heapsort; effectively a couple function pointers. For each type to sort, derive a trivial wrapper from the pure virtual base wrapper, and call the single instance of the sort template.

All this is reflexive and easy for someone familiar with C++, and it is really powerful. Its a small amount of code to do efficient sorting without code bloat.

But the point of my post was: most applicants aren't at that level, and will struggle with stuff like this...

Thanks, Best Regards, Dave

Reply to
Dave Nadler

You might have been using a broken implementation of quicksort, that didn't make sure to sort the smaller partition first in its recursive step. That keeps the stack use at O(log N) instead of potentially O(N), which in some really bad implementations can happen if the vector is already sorted.

It could be that embedded developers tend to be more into hardware than language geekery. You could look for programmers with a functional-programming background (ML or Haskell). C++ template generics may make more sense from that perspective.

Reply to
Paul Rubin

File size or stripped size? C++ tends to have ridiculously long identifier names, and tons of those. 'std::cout

Reply to
Stefan Reuther

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.