views:

679

answers:

9

I am working on a group senior project for my university and I have run into a major hurdle in trying to get my code to work.

The compiler that we have for our 8 bit Atmel microcontroller does not support the new or delete operators, and it does not support the C++ STL. I could program it in C, but I have to implement an A* algorithm which I have never done before. While I have tried C initially I soon realized that I never did pure C before. Trying to model objects with structs and functions is slowing me down since I am so used to the much cleaner C++ syntax.

Regardless, the exact wording for my compilers shortcomings can be found here: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus

To overcome them and still use C++ I have considered the following possibilities. 1) Don't allocate anything, just use templates to generate fixed arrays on the stack. 2) Allocate and find some hack to call the constructor for objects once I have allocated the space for them. Placement new isn't an option since new isn't an operator. 3) Just use C and suck it up, its a microcontroller why am I getting fancy? 4) Find a better compiler which will probably cost $$$.

The second option is the hardest but it would have the biggest pay off in terms of how I can write this code. However, I imagine that debugging it could be a huge pain if I get it wrong. I'm thinking of creating objects on the stack, copying their bits into the allocated space, and then zeroing the bits in the object so it doesn't call its destructor. To do that I would access the bits directly with an unsigned char pointer and the sizeof operator to get the byte count.

That sounds terrible and I don't know if it could work reliably, but I am considering it. I know vtables can be a problem but I don't intend on having any vtables since it is just an 8 bit microcontroller.

+2  A: 

Why not write it first on your desktop computer, taking into consideration the limitations of the compiler, debug it, make sure it works perfectly and only then move to the embedded environment?

shoosh
I don't think this is a very good idea because there are so much limitations and differences on embedded platforms (few RAM, 8 bits instead of 32 bits) so you will never get it to work that way.
rstevens
+17  A: 

Don't fight your tools. If the only compiler you have for your embedded system is a C compiler, learn C - it's not difficult. Trying to produce some bastardised version of the two languages just to solve a fairly simple programming problem will only end in tears.

To look at it another way, if your embedded platform didn't even support a C compiler, but only an assembler, would your first impulse be to sit down and write a C++ compiler in assembler? I hope not, I hope you would instead sit down and learn to use the assembler to complete your assignment - writing a C++ compiler (or even a C compiler) would be totally inappropriate use of your time, and would almost certainly result in failure.

anon
Although I agree that fighting ones tools is silly, investments in tool development normally pays itself. If, and when I've used assembler writting a small preprocessor based DSL was well worth it. However I'd never go as far as writting a C++ compiler :)
Robert Gould
Oh, I agree, and back when I wrote assembler (a long time ago) I used macro libraries I'd written. And I did actually write half of a C compiler in Z80 assembler (not to solve a particular problem though) before I came to my senses, re-wrote it in C and cross compiled it. It still din't work, but it was much less work :-)
anon
And don''t forget that the most embedded C compilers come only with a very limited sub set of the C library. One of the important parts I think they lack of is heap management (malloc/free).
rstevens
My compiler supports all of C, and the link provided states that it supports C++. The only problem is that it doesn't support everything. But I just managed to get an operator new overload to compile. I don't have the hardware with me to test it, but I think I have a solution. Using C will only take me more time as I much more well versed in C++ with RAII and template metaprogramming tricks, than I am in C. Plus the A* example code I have is C++.
Bernard
If you only need a single library from c++, in my opinion it should be easy to port the c++ code to c. One trick I find useful is to rename each constructor from NAME to new_NAME. Do the same for the destructors.
Trevor Boyd Smith
+3  A: 

Just because it doesn't have these tools doesn't mean you can't benefit from C++. If the project is large enough, access to Object Oriented design alone could be motivation enough.

If it doesn't support 'new' then it's probably because it doesn't make sense to make an automatic distinction between a heap and the stack. This might be because of your memory configuration. It might also be because memory resources are so constrained only very careful allocation makes sense. If you absolutely have to implement your own 'new' operator, you might look into adapting Doug Lea's malloc. I believe he began his allocator in a similar circumstance (reimplementing C++'s new).

I love the STL but it's still possible to do useful stuff without it. Depending on the scope of the project you might be better off just using an array.

Waylon Flinn
+6  A: 

Just for the record, zeroing the bits in an object won't affect whether the destructor gets called (unless the compiler has a special quirk that enables this behaviour). Just write some logging statements in your destructor to test this out.

Structuring your program not to allocate anything is probably the way the system was designed. I've not worked with embedded systems before, however I have read some experienced embedded shops that discourage use of dynamic memory because the runtime environment has scarce amounts of it.


However, if you must, you can still use placement new. If you don't have the <new> header, here are the relevant lines directly from it on my version of GCC:

// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) throw() { return __p; }
inline void* operator new[](std::size_t, void* __p) throw() { return __p; }

// Default placement versions of operator delete.
inline void  operator delete  (void*, void*) throw() { }
inline void  operator delete[](void*, void*) throw() { }

Stick that somewhere in a header file included by every source file that uses placement new/delete.

Sample file that tests this:

#include <cstdio>
#include <new>

int
main(int argc, char** argv)
{
    typedef char const* cstr;
    char foobar[16];
    cstr* str = new (&foobar) cstr(argc > 1 ? argv[1] : "Hello, world!");
    std::puts(*str);
    str->~cstr();
}

On my version of GCC, this does not use libstdc++ at all (if -fno-exceptions is used).


Now, if you want to combine that with malloc (if your platform provides this), then you can do this:

#include <cstdio>
#include <cstdlib>

inline void* operator new  (std::size_t n) {return std::malloc(n);}
inline void* operator new[](std::size_t n) {return std::malloc(n);}
inline void  operator delete  (void* p) {std::free(p);}
inline void  operator delete[](void* p) {std::free(p);}

int
main(int argc, char** argv)
{
    typedef char const* cstr;
    cstr* str = new cstr(argc > 1 ? argv[1] : "Hello, world!");
    std::puts(*str);
    delete str;
}

This allows you to use the standard new/delete that you're familiar with, without requiring use of libstdc++.

Good luck!

Chris Jester-Young
Wow, it looks like that works. I couldn't use the headers you supplied but those operator functions seem to work. From what I've read about those operators new[] and delete[] need to know the array size to call all the needed destructors, but in my test all of the destructors needed were called.
Bernard
+2  A: 

I had a similar compiler that implemented a bizarre version of the Embedded-C++ standard. We had operator new which would call constructors for us and destructors were called in most cases. The compiler/runtime vendor went and implemented try and catch using setjmp and longjmp as a convenience to the engineer. The problem was that they never mentioned that a throw would not cause destructors of local objects to be invoked!

Anyway, our group inherited the code base after someone wrote an application acting like it was Standard C++: using RAII techniques and all of the other goodness. We ended up rewriting it in what a number of us call object-oriented C instead. You might want to consider just biting the bullet and writing in straight C. Instead of constructors, have an explicitly called initialization method. Destructors become an explicitly called termination method. There isn't much of C++ that you can't mimic in C pretty quickly. Yes, MI is a pain in the ... but single inheritance is pretty easy. Take a look at this PDF for some ideas. It almost describes the approach that we took. I really wish I had written our method down somewhere...

D.Shawley
A: 

when doing embedded work, I once couldn't even link the C runtime for memory constraints, but the hardware had a DMA (dynamic memory allocator) instruction so I wrote my own malloc with that hardware, your hardware likely has a similar feature, so you could write a malloc and then a new based on the malloc.

Anyways in the end I used 99% stack allocations, and a few limits sets os static objects that I would recycle, by builiding in place. This might me a good solution.

Robert Gould
+5  A: 

I think you are approaching the problem from a viewpoint that is less than optimum.

You are focusing on the compiler (or lack thereof) instead of focusing on the HARDWARE.

The most probable answer to your main questions is "because the hardware doesn't support all that C++ stuff". Embedded hardware (microcontrolers) are noted for the customization of the hardware design - memory maps, interrupt handlers, I/O, etc.

In my opinion, you should FIRST spend some time with the hardware book for the microcontroller, learning the ins and outs of the device - i.e. how it was designed and for what primary purpose. Some were designed for fast memory manipulation, some for fast I/O handling, some for A/D type work, some for signal processing. The type of microcontroller dictates the assembler instructions they wrote for it, and that dictates what any higher-level compiler can do efficiently.

If this is important, spend some time to look at the assembler as well - it will tell you what the designers considered important. It will also tell you a lot about how much you can get from a high-level compiler.

Generally, microcontrollers don't support C++ because the design really doesn't care about objects, or fancy memory handling (from the C++ perspective). It can be done, but you are often trying to pound a round peg in a square hole to get constructors and destructors (and 'new' and 'delete') to work in the micro environment.

IF you have a C compiler for this unit, consider it a blessing. A good C compiler is often "more than enough" to create excellent embedded software.

Cheers,

-Richard

Huntrods
+1  A: 

Learn C, and be a better coder for it.

Paul Nathan
+1  A: 

You may find some helpful code on my A* tutorial website. Although the code I wrote to support this uses STL in should be easy to strip the STL support out. In addition there is a pool allocator included with it (fsa.h) that I wrote to speed up STL on game consoles. It is C++ code, but I ported it originally from C and I don't think it would be hard to do it the other way. The code is tested by over 10,000 people so it's a good base to start from.

Replacing the STL structures I'm using is no problem since it is limited to Vectors. I use one of the vectors as a priority queue using the heap functions (make_heap and push_heap). You can replace that with my old C code which has a priority queue implemented in C that should just drop into your code. (Which only does one alloc, so you can replace that with a pointer to a reserved area of your memory.

As you can see in this code fragment from the header, the main difference in C code is that there's no this pointer, no object, so your code typically takes an object pointer as the first argument.

void PQueueInitialise( PQUEUE *pq, int32 MaxElements, uint32 MaxRating, bool32 bIsAscending );
void PQueueFree( PQUEUE *pq );
int8 PQueuePush( PQUEUE *pq, void *item,  uint32 (*PGetRating) ( void * ) );
int32 PQueueIsFull( PQUEUE *pq );
int32 PQueueIsEmpty( PQUEUE *pq );
void *PQueuePop( PQUEUE *pq, uint32 (*PGetRating) ( void * ) );
justinhj
Your "old C code" calls malloc(). I don't think there are that much C compilers for 8 bit embedded platforms that support enough of the C library to give you heap management (malloc/free)!
rstevens
If you look at it more carefully it calls malloc to allocate a block of memory for a fixed size priority queue. The queue itself then uses this single block very efficiently. So it can be converted to not use malloc with one line of code.
justinhj
Who down voted this? This is useful stuff, I need A* help too. I have some Google code and a book on AI but I'm still figuring it all out.
Bernard