在用C++编写了几年后,我最近在嵌入式领域的C中提供了一个编码工作.
抛开在嵌入式领域中忽略C++是对还是错的问题,C++中有一些特性/习惯用法我会错过很多.仅举几个:
从C++到C的经历是什么?
您找到的C替代品是您最喜欢的C++特性/习语吗?您是否发现了C++的C功能?
Mik*_*one 65
在一个嵌入式项目上工作,我尝试过在所有C中工作一次,但是无法忍受.它只是如此冗长以至于难以阅读任何内容.此外,我喜欢我编写的优化嵌入式容器,这些容器必须变得更安全,更难以修复#define
块.
C++中的代码看起来像:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
Run Code Online (Sandbox Code Playgroud)
变成:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1);
Run Code Online (Sandbox Code Playgroud)
许多人可能会说这很好,但如果你不得不做一对"方法"调用,那就太荒谬了.两行C++将变为C中的五行(由于80-char行长度限制).两者都会生成相同的代码,因此它不像目标处理器那样关注!
有一次(早在1995年),我尝试为很多处理器数据处理程序编写了大量的C语言.每个处理器都有自己的内存和程序的类型.供应商提供的编译器是一个C编译器(某种类型的HighC衍生版本),它们的库是封闭源代码,因此我无法使用GCC进行构建,并且它们的API设计时考虑到您的程序主要是初始化/进程/终止多样性,因此处理器间通信充其量只是基本的.
我放弃了大约一个月,发现了cfront的副本,并将其入侵到makefile中,因此我可以使用C++.Cfront甚至不支持模板,但C++代码更清晰.
C对模板最接近的是声明一个包含大量代码的头文件,如下所示:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ /* ... */ }
Run Code Online (Sandbox Code Playgroud)
然后把它拉进去:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
Run Code Online (Sandbox Code Playgroud)
请注意,这不适用于复合类型(例如,没有队列unsigned char
),除非您typedef
先创建.
哦,请记住,如果这个代码实际上没有在任何地方使用,那么你甚至不知道它的语法是否正确.
编辑:还有一件事:你需要手动管理代码的实例化.如果你的"模板"代码不是所有的内联函数,那么你必须加入一些控制来确保事物只被实例化一次,这样你的链接器就不会吐出一堆"Foo的多个实例"错误.
为此,您必须将非内联内容放在头文件的"实现"部分中:
#ifdef implementation_##TYPE
/* Non-inlines, "static members", global definitions, etc. go here. */
#endif
Run Code Online (Sandbox Code Playgroud)
然后,在每个模板变体的所有代码中的一个位置,您必须:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
Run Code Online (Sandbox Code Playgroud)
此外,这实现部分需要被外界标准#ifndef
/ #define
/ #endif
一长串,因为你可能包括另一头文件模板的头文件,但需要在以后实例.c
文件.
是的,它快速变得难看.这就是大多数C程序员甚至都不尝试的原因.
特别是在具有多个返回点的函数中,例如,不必记住在每个返回点上释放互斥锁.
好吧,忘记漂亮的代码并习惯你的所有返回点(除了函数的结尾)是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:;
}
/* Figure out `result` for real, then fall through to... */
Queue_##TYPE##_Top_exit:
Mutex_Lock(this->lock);
return result;
}
Run Code Online (Sandbox Code Playgroud)
也就是说,你为MyClass写了一次,如果一个MyClass实例是MyOtherClass的成员,MyOtherClass就不必显式地取消初始化MyClass实例 - 它的自动调用它.
必须以相同的方式显式地处理对象构造.
这实际上是一个简单的解决方法:只需在每个符号上添加一个前缀.这是我之前谈到的源膨胀的主要原因(因为类是隐式命名空间).C人们一直生活在这里,永远,并且可能不会看到重要的是什么.
因人而异
Jen*_*edt 16
我之所以从C++转到C是出于不同的原因(某种过敏反应;)而且我只想念了一些东西,以及我获得的一些东西.如果您坚持使用C99,如果可以的话,有一些结构可以让您非常安全地编程,特别是
for
-scope变量可以帮助您进行范围绑定资源管理,特别是确保unlock
互斥锁或free
数组,即使在初步函数返回时也是如此__VA_ARGS__
宏可用于为函数提供默认参数并执行代码展开 inline
结合良好的函数和宏来替换(类型)重载函数小智 8
C和C++之间的区别在于代码行为的可预测性.
更准确地预测您的代码在C中会做什么,在C++中,提出精确预测可能会变得有点困难.
C中的可预测性使您可以更好地控制代码的工作,但这也意味着您必须执行更多操作.
在C++中,你可以编写更少的代码来完成同样的事情,但是(对我而言)我偶尔会知道目标代码是如何在内存中布局的,而且是预期的行为.
在我的工作中 - 顺便说一下,我是嵌入式的 - 我经常在C和C++之间来回切换.
当我在C时,我想念C++:
模板(包括但不限于STL容器).我将它们用于特殊计数器,缓冲池等等(构建我自己的类模板库和我在不同嵌入式项目中使用的函数模板)
非常强大的标准库
析构函数,当然可以使RAII成为可能(互斥,中断禁用,跟踪等)
访问说明符,以更好地强制谁可以使用(不看)什么
我在较大的项目上使用继承,并且C++对它的内置支持比将基类作为第一个成员嵌入的C"hack"更清晰和更好(更不用说构造函数的自动调用,init.列表等等). )但上面列出的项目是我最想念的项目.
另外,我工作的嵌入式C++项目可能只有大约三分之一使用异常,所以我已经习惯了没有它们的生活,所以当我回到C时,我不会错过太多.
另一方面,当我回到一个拥有大量开发人员的C项目时,我会习惯向那些消失的人解释C++问题.主要是由于C++的复杂性引起的问题,以及那些认为自己知道发生了什么的人,但他们确实处于C++置信度曲线的"C with classes"部分.
考虑到这个选择,我更喜欢在项目中使用C++,但前提是团队在语言上非常扎实.当然,假设它不是一个8KμC项目,无论如何我都在写"C".