How can you do C++ when your embedded compiler doesn't have operator new or STL support?

三世轮回 提交于 2019-12-03 14:45:41

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!

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.

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

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.

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

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

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?

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!