我打算编写一个代码库来访问低级别的某些硬件(即翻转寄存器位等)。
以前,我将所有内容都写成C函数,并使用extern“C”来编译C和C ++代码的库。因此,C和C ++用户只需要包含头文件并按原样调用函数。
现在,我正在考虑将事情组织成课程。例如,我可以将所有功能放在一个类中初始化,配置,发送和接收UART。这在C ++中运行良好,但C怎么样?我不能把“C”作为整个班级。
我想到的一件事是:用extern“C”转义标准C函数中的所有内容。然后,为C ++提供一个包装类,它有一堆内联方法来调用这些'C'函数。
int foo_bar (int *address, int data) {...} // extern C stuff
int foo::bar (int *address, int data) { return foo_bar(address, data); } // inline method
可以吗?还有其他想法吗?最佳做法?
答案 0 :(得分:5)
你提出的建议有一些先例 - 微软的MFC类只是C兼容的Windows API的C ++包装。
在你开始之前,除了为自己创造繁忙的工作之外,你应该考虑一些目标。 C ++应该比C更容易使用,或者你没有获得任何东西。
答案 1 :(得分:3)
这样做的一个可靠理由是,如果您的C接口使用典型的“句柄”惯用语来表示资源。
OpaqueThingHandle t = CreateOpaqueThing();
DoStuffWithOpaqueThing(t);
DestroyOpaqueThing(t);
在C中,为了实现信息隐藏,OpaqueThingHandle
通常是void *
的typedef,因此客户端无法了解其实现方式。
C ++包装器将能够通过将RAAI映射构造和销毁应用于获取或释放句柄类型标识的资源的函数来添加真正有用的东西:
class OpaqueThing : boost::noncopyable
{
OpaqueThingHandle handle;
public:
OpaqueThing()
: handle(CreateOpaqueThing()) {}
~OpaqueThing()
{ DestroyOpaqueThing(handle); }
void DoStuff()
{ DoStuffWithOpaqueThing(handle); }
};
答案 2 :(得分:2)
你可以这样做,但它能带给你什么?除非该类增加了一些功能,否则我会坚持使用自由函数方法。
另一方面,通过对类客户端管理缓冲区等操作,类方法可以使C函数更容易使用 - 类仍然使用C API来完成实际工作
答案 3 :(得分:2)
您需要导出基于C方法和C ++类的接口。 您可以采用两种方式 - 围绕C函数的简化C ++包装,或围绕C ++实例的C函数。
对于后者,典型的模式是:
void * c_open_thing(id) { return new CThing(id); }
void c_close_thing(void * handle) { delete (CThing) handle; }
int c_transmit(void * handle, transmitbuf)
{ return ((CThing *)handle)->Transmit(transmitbuf); }
这样一个简单的包装器是没有意义的,但是你没有做到这一点。 C ++包装器可以通过以下方式增加值:
执行施工/销毁要求
除非你能提供,否则在这里使用cosntructor /析构函数通常是不够的
作业和复制结构。我通常使用reference counted handle。
错误处理
这可能需要清理,将错误转换为(有意义的!)例外等。
线程安全 当然,如果你控制原始库,你可以在那里添加它。
...
答案 4 :(得分:1)
我知道您希望从C ++的抽象功能中受益,但仍然可以使用C编写的代码访问您的代码。实现此目的的一种方法是用C ++编写大量代码然后创建一组瘦extern“C”包装函数,用于将代码与C世界连接。
你建议的方法也有效,但是,正如另一位受访者所说,它不会给你任何额外的力量。
请注意,与普通C函数方法相比,这两种方法都会带来轻微的性能损失。在您的提案中,C ++代码支付了价格,在我的C代码支付价格。这可以通过将函数定义为内联来最小化。
答案 5 :(得分:1)
坦率地说,我看不出基本上重复的C和C ++函数会给你带来什么。
我建议尽可能保持C函数的基本功能,定义一个API,其中所有基元都暴露出来并且没什么特别的。然后在更高级别定义C ++类,因此UART将具有构造函数,析构函数,有意义的方法等等。它将拥有所有必要的数据存储。 C ++类不会暴露所有可能的原语;为了做一些不同的事情,你要编写一个新的成员函数或类或其他什么,调用C API。
这就是OCCI(Oracle C ++调用接口)和MFC等系统中的方法,它运行良好。