在准备一个库(让我们称之为libfoo)时,我发现自己面临以下两难困境:我是否将其编写为带有C包装器的C ++库:
namespace Foo {
class Bar {
...
};
}
/* Separate C header. #ifdef __cplusplus omitted for brevity. */
extern "C" {
typedef void *FooBar;
FooBar* foo_bar_new() { return new Foo::Bar; }
void foo_bar_delete(FooBar *bar) { delete bar; }
}
或者将它编写为带有C ++包装器的C库更好:
/* foo/bar.h. Again, #ifdef __cplusplus stuff omitted. */
typedef struct {
/* ... */
} FooBar;
void foo_bar_init(FooBar *self) { /* ... */ }
void foo_bar_deinit(FooBar *self) { /* ... */ }
/* foo/bar.hpp */
namespace Foo {
class Bar {
/* ... */
FooBar self;
}
Bar::Bar() {
foo_bar_init(&self);
}
Bar::~Bar() {
foo_bar_deinit(&self);
}
}
您更喜欢哪个,为什么?我赞成后者,因为这意味着我不必担心我的C函数会意外地冒出异常,而且我更喜欢C作为一种语言,因为我觉得它是一个较小的语义雷区。别人怎么想?
编辑:这么多好的答案。谢谢大家。遗憾的是我只能接受一个。答案 0 :(得分:19)
小点:
当你编写C库时,它在任何地方都很有用 - 在C中,在C ++中(包装器)和许多其他语言,如Python,Java使用绑定等,最重要的是它只需要C运行时。
当你编写C ++包装器时,你还需要编写一个C包装器,但它并不像你想象的那么简单,例如:
c_api.h:
extern "C" {
typedef void *Foo;
Foo create_foo();
}
c_api.cpp:
void *create_foo()
{
return new foo::Foo();
}
有什么问题?它可能会扔!并且程序将崩溃,因为C没有堆栈 解开语义。所以你需要这样的东西:
void *create_foo()
{
try {
return new foo::Foo();
}
catch(...) { return 0; }
}
这适用于每个 C ++ api函数。
所以我认为编写C库并提供单独的 C ++包装器是更好的解决方案。
此外,它不需要与C ++运行时库链接。
答案 1 :(得分:8)
使用您喜欢编写库的语言编写库。从技术上讲,您包装的方式并不重要。虽然有些C项目的目的可能是排除不是C的库,而C ++项目排除用C语言编写的库是很奇怪的,但这主要是哲学上的反对而不是实际的反对。
在C ++包装器中包装C可能会导致稍微更大的包装器,但C程序员更容易接受。
请注意,如果要分发二进制文件,C的简单性是有利的。
答案 2 :(得分:5)
如果您更喜欢用C语言编写,为什么还需要C ++包装器? C ++客户端可以使用C风格的API接口。另一方面,您更喜欢C ++,有必要为C客户端使用C包装器。
答案 3 :(得分:4)
如果你的lib必须作为二进制+标题分发(而不是发布源代码),你会发现 一个C API更普遍可链接 ,因为C通常是任何平台上最小的通用API。
这就是为什么我通常不得不为我在过去十年中需要API的项目制作带有内联C ++包装器的 C API 。由于这些程序都是用C ++编写的,这意味着我必须围绕C ++代码创建一个C包装器API,只是为了在它周围放置另一个包装C ++ API 。
答案 4 :(得分:3)
假设没有链接时优化的编译,C编译器不能内联包装函数,因为它不知道如何处理C ++调用 - 但是C ++编译器可以很容易地内联C调用。
因此,为C库创建C ++包装器可能是个好主意,而不是相反。
答案 5 :(得分:2)
我个人更喜欢使用C ++并将其包装到C. 但事实上,这是一个品味问题,你必须自己决定如何做到这一点。如果你觉得用C语言编写库感觉更舒服,那么就把它包装成C ++。
关于异常:您可以在为C包装的每个函数中捕获它们,并为它们返回错误代码,例如:有一个自己的异常类,它已经有一个数字错误代码值,你可能会返回到你的C函数,其他可能被其他库抛出的异常类可以被翻译成其他东西,但是你应该早点抓住它们。
答案 6 :(得分:2)
如果您对使用C语言编写库感到满意,那就去做吧。作为C库,它将更具可移植性,并且如您所述,没有异常问题。从C ++库开始并将其包装在C中是不常见的。
答案 7 :(得分:2)
这也很大程度上取决于您计划在库中使用的内容。如果它反过来可以从其他C ++库中获益,那就使用C ++。
也可以说,如果你的库非常大(内部,不一定是API),那么用C ++实现它会更容易。 (这不是我的一杯茶,我更喜欢C,但有些人发誓C ++。)
另外请记住,C ++使用的运行时非常需要操作系统,以支持异常。
如果您想要将您的库用作操作系统的基础,或者在没有操作系统的环境中使用,您必须知道如何禁用异常支持,避免大量(全部?)STL和提供自己的分配器和解除分配器。这不是不可能,但你需要确切地知道你做了什么。
C更适合那些低级别的东西。
答案 8 :(得分:1)
我个人更喜欢用C ++编写,然后使用包装器公开C接口。 主要是因为我宁愿用适当的OO语言写作。我也使用了OO风格的C包装器,我在这篇文章中概述了我在这篇文章中写了一篇非常详细的解释,说明你需要从C调用OO C ++ Developing C wrapper API for Object-Oriented C++ code