在做一个嵌入式项目时,我试着用全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年),我try 为多处理器数据处理程序编写大量C语言.每个处理器都有自己的内存和程序.供应商提供的编译器是一个C编译器(某种HighC衍生工具),它们的库是封闭源代码的,所以我无法使用GCC来构建,它们的API的设计理念是,您的程序主要是initialize/process/terminate类型,所以处理器间通信充其量只是初级的.
我在放弃之前大约一个月,找到了一个cfront的副本,并把它存入MaFiges文件中,这样我就可以使用C++了.CWORD甚至不支持模板,但是C++代码要清晰得多.
通用的、类型安全的数据 struct (使用模板).
C最接近模板的是声明一个头文件,其中包含大量代码,如下所示:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ /* ... */ }
然后把它拉进go ,比如:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
请注意,这不适用于复合类型(例如,没有unsigned char
个队列),除非您先 Select typedef
.
哦,记住,如果这个代码实际上没有在任何地方使用,那么你甚至不知道它的语法是否正确.
EDIT:还有一件事:您需要manually管理代码的实例化.如果您的"模板"代码不是all个内联函数,那么您必须加入一些控制来确保只实例化一次,这样您的链接器就不会抛出一堆"多个Foo实例"错误.
要做到这一点,您必须将非内联内容放在头文件的"Implementation"部分中:
#ifdef implementation_##TYPE
/* Non-inlines, "static members", global definitions, etc. go here. */
#endif
然后,在所有代码per template variant的one位中,您必须:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
此外,此实现部分需要是标准#ifndef
/#define
/#endif
系列的outside,因为您可能会将模板头文件包含在另一个头文件中,但随后需要在.c
文件中实例化.
是的,很快就会变丑.这就是为什么大多数C程序员甚至不try .
拉伊.
尤其是在具有多个返回点的函数中,例如,不必记住在每个返回点上释放互斥.
好吧,忘记你漂亮的代码,习惯所有的返回点(函数末尾除外)
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:;
}
/* Figure out `result` for real, then fall through to... */
Queue_##TYPE##_Top_exit:
Mutex_Lock(this->lock);
return result;
}
一般来说是析构函数.
也就是说,您为MyClass编写了一次d'tor,然后如果MyClass实例是MyOtherClass的成员,MyOtherClass不必显式地go 初始化MyClass实例——它的d'tor会自动调用.
必须以同样的方式显式处理对象构造.
名称空间.
这实际上很容易解决:只需在every个符号上添加一个前缀即可.这是我前面提到的源代码inflating 的主要原因(因为类是隐式名称空间).C组的人一直都在这样生活,很可能看不出有什么大不了的.
YMMV