在嵌入式项目中,我曾经尝试在所有C语言中工作,但受不了。太冗长了,以至于很难阅读任何东西。另外,我喜欢我编写的针对嵌入式优化的容器,这种容器必须变得安全性低得多,并且很难修复#define
块。
C ++中的代码如下所示:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
变成:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1)
很多人可能会说这很好,但是如果您必须要做多个而不是一行中的“方法”调用,这将变得很荒谬。两行C ++将变成五行C(由于80个字符的行长限制)。两者都会生成相同的代码,因此与目标处理器无关!
有一次(早在1995年),我尝试为多处理器数据处理程序编写大量的C语言。每个处理器都有自己的内存和程序的类型。供应商提供的编译器是C编译器(某种HighC派生类),它们的库是封闭源代码,因此我无法使用GCC进行构建,并且其API的设计思想是程序将主要是初始化/进程/终止多样性,因此处理器间通信充其量是基本的。
我放弃了大约一个月的时间,找到了cfront的副本,并将其入侵到makefile中,以便可以使用C ++。Cfront甚至不支持模板,但是C ++代码清晰得多。
通用的类型安全的数据结构(使用模板)。
C与模板最接近的事情是用许多代码声明一个头文件,如下所示:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ }
然后用类似的方式将其拉入:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
请注意,unsigned char
除非您typedef
先进行复合操作,否则这不适用于复合类型(例如,没有队列)。
哦,请记住,如果实际上没有在任何地方使用此代码,那么您甚至都不知道它在语法上是否正确。
编辑:另外一件事:您将需要手动管理代码的实例化。如果您的“模板”代码不是全部内联函数,那么您将必须进行一些控制以确保事物仅被实例化一次,以便您的链接器不会吐出一堆“ Foo的多个实例”错误。
为此,您必须将非内联的内容放在头文件的“实现”部分中:
#ifdef implementation_##TYPE
#endif
然后,在每种模板变体的所有代码中的某个位置,您必须:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
此外,这实现部分需求是外标准#ifndef
/ #define
/#endif
一长串,因为你可能包括另一头文件模板头文件,但需要实例化后的.c
文件。
是的,它变得很难看。这就是为什么大多数C程序员甚至不尝试的原因。
RAII。
特别是在具有多个返回点的函数中,例如,不必记住在每个返回点上释放互斥量。
好吧,忘记您的漂亮代码,并习惯于所有返回点(函数末尾除外)为goto
s:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{
TYPE * result;
Mutex_Lock(this->lock);
if(this->head == this->tail)
{
result = 0;
goto Queue_##TYPE##_Top_exit:;
}
Queue_##TYPE##_Top_exit:
Mutex_Lock(this->lock);
return result;
}
析构函数一般。
即,您为MyClass编写了一次d'tor,然后,如果MyClass实例是MyOtherClass的成员,则MyOtherClass不必显式取消初始化MyClass实例-它的d'tor会自动调用。
对象构造必须以相同的方式显式处理。
命名空间。
这实际上是一个简单的解决方法:只需在每个符号上添加前缀即可。这是前面提到的源代码膨胀的主要原因(因为类是隐式名称空间)。C人民一直在好好生活,直到永远,也许看不到有什么大不了的。
青年汽车