当嵌入式编译器没有运算符new或STL支持时,如何进行C ++?

我正在为我的大学开展一个小组高级项目,并且在尝试让我的代码工作时遇到了一个主要障碍。

我们的8位Atmel微控制器的编译器不支持new或delete运算符,它不支持C ++ STL。 我可以用C编程,但是我必须实现一个我从未做过的A *算法。 虽然我最初尝试过C,但我很快意识到我之前从未做过纯C。 尝试使用结构和函数对对象进行建模正在减慢我的速度,因为我已经习惯了更清晰的C ++语法。

无论如何,我的编译器缺点的确切措辞可以在这里找到: http : //www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus

为了克服它们并仍然使用C ++,我考虑了以下可能性。 1)不要分配任何东西,只需使用模板在堆栈上生成固定数组。 2)在为对象分配空间后,分配并找到一些hack来调用对象的构造函数。 由于new不是运营商,因此不能选择新的展示位置。 3)只需使用C并将其吸收,它是一个微控制器为什么我喜欢它? 4)找到一个更好的编译器,可能会花费$$$。

第二个选项是最难的,但就我如何编写此代码而言,它将获得最大的回报。 但是,我想如果我弄错了调试它可能是一个巨大的痛苦。 我正在考虑在堆栈上创建对象,将它们的位复制到分配的空间中,然后将对象中的位清零,这样它就不会调用它的析构函数。 为此,我将使用unsigned char指针和sizeof运算符直接访问这些位以获取字节数。

这听起来很糟糕,我不知道它是否可以正常工作,但我正在考虑它。 我知道vtable可能有问题,但我不打算使用任何vtable,因为它只是一个8位微控制器。

只是为了记录,将对象中的位置零将不会影响析构函数是否被调用(除非编译器具有启用此行为的特殊怪癖)。 只需在析构函数中编写一些日志语句来测试它。

构建程序不分配任何东西可能就是系统的设计方式。 我之前没有使用嵌入式系统,但是我已经阅读了一些经验丰富的嵌入式商店,这些商店不鼓励使用动态内存,因为运行时环境的数量很少。


但是,如果必须,您仍然可以使用新的展示位置。 如果你没有标题,这里是我的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() { } 

坚持使用放置新/删除的每个源文件包含的头文件中的某处。

测试这个的示例文件:

 #include  #include  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(); } 

在我的GCC版本中,根本不使用libstdc++ (如果使用-fno-exceptions )。


现在,如果你想将它与malloc结合起来(如果你的平台提供了这个),那么你可以这样做:

 #include  #include  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; } 

这允许您使用您熟悉的标准new / delete ,而无需使用libstdc++

祝好运!

不要打你的工具。 如果你的嵌入式系统唯一的编译器是C编译器,那么学习C – 并不困难。 为了解决一个相当简单的编程问题,试图生成两种语言的一些混合版本只会流下眼泪。

换句话说,如果您的嵌入式平台甚至不支持C编译器,而只支持汇编程序,那么您的第一个冲动是坐下来在汇编程序中编写C ++编译器吗? 我希望不会,我希望你坐下来学习使用汇编程序来完成你的任务 – 编写一个C ++编译器(甚至是一个C编译器)将完全不合适地利用你的时间,几乎肯定会导致失败。

我认为你从一个不太理想的观点来看待这个问题。

您专注于编译器(或缺少编译器)而不是专注于硬件。

对你的主要问题最可能的答案是“因为硬件不支持所有C ++的东西”。 嵌入式硬件(微控制器)以硬件设计的定制而着称 – 存储器映射,中断处理程序,I / O等。

在我看来,你应该花一些时间在微控制器的硬件书上,学习设备的细节 – 即它是如何设计的以及主要用途。 一些设计用于快速存储器操作,一些用于快速I / O处理,一些用于A / D类型工作,一些用于信号处理。 微控制器的类型决定了他们为它编写的汇编程序指令,并规定了任何更高级别的编译器可以有效地执行的操作。

如果这很重要,那么花点时间看一下汇编程序 – 它会告诉你设计师认为重要的东西。 它还会告诉您很多关于从高级编译器可以获得多少的信息。

通常,微控制器不支持C ++,因为设计实际上并不关心对象或花哨的内存处理(从C ++的角度来看)。 它可以做到,但你经常试图在一个方孔中敲一个圆钉以使构造函数和析构函数(以及’new’和’delete’)在微环境中工作。

如果你有一个C编译器为这个单位,请认为这是一个祝福。 一个好的C编译器通常“绰绰有余”来创建出色的嵌入式软件。

干杯,

-Richard

仅仅因为它没有这些工具并不意味着你无法从C ++中受益。 如果项目足够大,单独访问面向对象的设计就足够了。

如果它不支持’new’,那么可能是因为在堆和堆栈之间进行自动区分是没有意义的。 这可能是因为您的内存配置。 也可能是因为内存资源受到如此限制,只有非常谨慎的分配才有意义。 如果你必须实现自己的“新”运算符,你可能会考虑改编Doug Lea的malloc 。 我相信他在类似的情况下开始他的分配器(重新实现C ++的新function)。

我喜欢STL,但如果没有它,它仍然可以做有用的东西。 根据项目的范围,您可能最好只使用数组。

我有一个类似的编译器,实现了嵌入式C ++标准的奇怪版本。 我们有operator new ,它会为我们调用构造函数,并且在大多数情况下都会调用析构函数。 编译器/运行时供应商使用setjmplongjmp实现了trycatch ,以方便工程师 。 问题是他们从未提到过throw不会导致调用本地对象的析构函数!

无论如何,在有人编写了类似于标准C ++的应用程序之后,我们的小组inheritance了代码库:使用RAII技术和所有其他优点。 我们最终用许多我们称之为面向对象的C来重写它。 您可能想要考虑只是咬住子弹并直接写入C.而不是构造函数,有一个显式调用的初始化方法。 析构函数成为一种明确称为终止方法。 没有太多的C ++你不能很快模仿C语言。 是的,MI是一个痛苦…但单一inheritance很容易。 看一下这个PDF的一些想法。 它几乎描述了我们采用的方法。 我真的希望我把方法写到某个地方……

您可以在我的A *教程网站上找到一些有用的代码。 虽然我写的支持这个使用STL的代码应该很容易剥离STL支持。 另外还有一个包含它的池分配器(fsa.h),我写这篇文章是为了加速游戏机上的STL。 它是C ++代码,但我最初是从C语言移植而来的,我不认为以其他方式做到这一点并不困难。 该代码经过10,000多人的测试,因此它是一个很好的基础。

替换我正在使用的STL结构没有问题,因为它仅限于Vector。 我使用堆函数(make_heap和push_heap)将其中一个向量用作优先级队列。 您可以将其替换为我的旧C代码 ,该C代码具有在C中实现的优先级队列,该代码应该放入您的代码中。 (只有一个alloc,所以你可以用指向内存保留区域的指针替换它。

正如你在头部的代码片段中看到的那样,C代码的主要区别在于没有这个指针,没有对象,所以你的代码通常将一个对象指针作为第一个参数。

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

为什么不首先在台式计算机上编写它,考虑到编译器的限制,调试它,确保它完美运行,然后才转移到嵌入式环境?

在进行嵌入式工作时,我曾经甚至无法将C运行时链接到内存约束,但是硬件有一个DMA(动态内存分配器)指令,所以我用自己的硬件编写了自己的malloc,你的硬件可能有类似的function,所以你可以写一个malloc然后一个基于malloc的新东西。

无论如何最后我使用了99%的堆栈分配,并且一些限制设置了我将通过建立到位来回收的静态对象。 这可能是一个很好的解决方案。