使用 C 和 C++ 编写库接口,以哪种方式包装?
在准备一个库(我们称之为 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 作为一种语言,因为我觉得它是一个较小的语义雷区。其他人怎么看?
编辑:这么多好的答案。谢谢大家。可惜我只能接受一个。
When preparing a library (let's call it libfoo), I find myself presented with the following dilemma: do I write it as a C++ library with a C wrapper:
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; }
}
Or is it better to write it as a C library with a C++ wrapper:
/* 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);
}
}
Which do you prefer, and why? I favour the latter because it means I don't have to worry about my C functions accidentally having exceptions bubble up, plus I prefer C as a language as I feel that it's a smaller semantic minefield. What do other people think?
EDIT: So many good answers. Thanks all. It's a shame that I can only accept one.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
小要点:
当您编写 C 库时,它在任何地方都很有用 - 在 C、C++(带有包装器)和许多其他语言(如 Python、使用绑定的 Java 等)中,最重要的是它只需要 C 运行时。
当你编写C++包装器时,你还需要编写一个C包装器,但它并不像你想象的那么简单,例如:
c_api.h:
c_api.cpp:
有什么问题吗?它可能会抛出!由于C没有堆栈,程序会崩溃
展开语义。所以你需要这样的东西:
对于每个 C++ api 函数来说。
所以我认为编写一个 C 库并提供一个单独 C++ 包装器是更好的解决方案。
而且它不需要与 C++ 运行时库链接。
Small points:
When you write C library it is useful anywhere - in C, in C++ (with wrapper) and many other languages like Python, Java using bindings etc and most important it requires only C runtime.
When you write C++ wrapper you also need to write a C wrapper, but it is not as simple as you think, for example:
c_api.h:
c_api.cpp:
What is wrong? it may throw! and the program will crash as C does not have stack
unwinding semantics. So you need something like:
And this for every C++ api function.
So I think that writing a C library and providing a separate C++ wrapper is better solution.
Also it would not require linking with C++ runtime library.
用您喜欢编写库的语言编写库。从技术上讲,采用哪种包装方式并不重要。尽管某些 C 项目可能旨在排除非 C 的库,而 C++ 项目排除用 C 编写的库会很奇怪,但这主要是一种哲学上的反对,而不是实际的反对。
将 C 封装在 C++ 包装器中可能会导致包装器稍大一些,但更容易被 C 程序员接受。
请注意,如果您要分发二进制文件,C 的简单性是有利的。
Write the library in the language you prefer to write libraries in. It doesn't technically much matter which way you wrap. Although some C projects may aim to exclude libraries that aren't C whereas it'd be odd for a C++ project to exclude libraries written in C, that's mostly a philosophical objection than a practical one.
Wrapping C in a C++ wrapper will likely result in a slightly larger wrapper but be more acceptable to C programmers.
Note that if you are distributing binaries, C's simplicity is advantageous.
如果您更喜欢用 C 编写,为什么需要 C++ 包装器? C++客户端可以使用C风格的API接口。另一方面,如果您更喜欢 C++,则有必要为 C 客户端提供 C 包装器。
If you prefer to write in C, why do you need C++ wrapper? C++ client can use C-style API interface. On the other hand, you you prefer C++, it is necessary to have C wrapper for C clients.
如果您的库必须以二进制+标头的形式分发(而不是传送源代码),您会发现C API 更具有普遍可链接性,因为C 通常是任何平台上最小的通用 API。
这就是为什么我通常必须为我在过去十年中所做的需要 API 的项目制作带有内联 C++ 包装器的 C API。由于程序都是用 C++ 编写的,这意味着我必须围绕 C++ 代码创建一个 C 包装 API,只是为了在它周围放置另一个包装 C++ API。
If your lib will ever have to be distributed as a binary + header (instead of shipping the source code), you will find that a C API is more universally linkable, since C usually is the smallest common API on any platform.
This is why I usually had to make C APIs with inline C++ wrappers around them for the projects I did in the last decade that needed an API. Since the programs were all in C++, this meant I had to make a C wrapper API around C++ code, just to put another wrapping C++ API around it.
假设编译时没有链接时优化,C 编译器无法内联包装函数,因为它不知道如何处理 C++ 调用 - 但 C++ 编译器可以轻松内联 C 调用。
因此,为 C 库创建一个 C++ 包装器可能是一个好主意,而不是相反。
Assuming compilation without link-time optimizations, a C compiler can't inline the wrapper functions as it doesn't know how to handle C++ calls - but a C++ compiler can easily inline C calls.
Therefore, it might be a good idea to create a C++ wrapper for the C library, instead of the other way around.
我个人更喜欢使用 C++ 并将其包装为 C。
但事实上,这是一个品味问题,你必须根据自己的喜好做出决定。如果您觉得用 C 语言编写库更舒服,那么就使用它并将其包装为 C++。
关于异常:您可以在为 C 包装的每个函数中捕获它们并为其返回错误代码,例如通过拥有一个自己的异常类,该异常类已经有一个数字错误代码值,您可以将其返回到您的 C 函数,其他函数可能有任何其他库抛出的错误都可以转换为其他内容,但是无论如何您应该早点捕获它们。
I personally prefer to use C++ and would wrap it to C.
But infact it's a matter of taste and you'd have to make your own decision how you'd like it. If you feel more comfortable writing the library in C then go for it and wrap it for C++.
About the exceptions: You can catch them in every function wrapped for C and return an error code for them, by e.g. having an own exception class which has already a numeric error code value which you may return to your C functions, others which might have been thrown by any other libraries can be translated to something else, however you should have caught them earlier anyway.
如果您对用 C 语言编写库感到满意,那么就这样做。作为一个 C 库,它将更加可移植,并且不会出现您提到的异常问题。从 C++ 库开始并将其包装在 C 中的情况并不常见。
If you feel comfortable with writing your library in C then do it. It will be more portable as a C library and has no issues with exceptions as you mentioned. It is uncommon to start with a C++ library and wrap it in C.
就我个人而言,我更喜欢用 C++ 编写它,然后使用包装器公开 C 接口。
主要是因为我宁愿用适当的面向对象语言编写。我也会使用 OO 风格的 C 包装器,就像我在这篇文章中概述的那样,我在这篇文章中写了一个非常详细的解释,说明如何从 C 调用 OO C++ 为面向对象的 C++ 代码开发 C 包装器 API
Personally I prefer to write it in C++, then expose the C interface using a wrapper.
Mostly because I'd rather write in a proper OO language. I'd use an OO style C wrapper too, liek I outline in this post I wrote a pretty detailed explanation about what you need to d to call OO C++ from C in this post Developing C wrapper API for Object-Oriented C++ code
它还很大程度上取决于您计划在库中使用的内容。如果它又可以从其他 C++ 库中获益匪浅,那么就使用 C++。
也可能有人会说,如果您的库非常大(在内部,不一定是 API 方面),那么用 C++ 实现它会更容易。 (这不是我的菜,我更喜欢 C,但有些人非常喜欢 C++。)
还要记住,C++ 使用的运行时几乎需要操作系统来支持异常。
如果您设想将您的库用作操作系统的基础,或者在没有操作系统的环境中使用,您必须知道如何禁用异常支持,避免大量(全部?)STL 并提供您自己的分配器和解除分配器。这并非不可能,但您需要确切地知道自己在做什么。
C更适合那些低级的事情。
It also depends a lot on what you plan to use in your library. If it in turn could benefit greatly from other C++ libraries, then use C++.
It could also be argued, that if your library is going to be very big (internally, not necessarily API wise) it can be easier to implement it in C++. (It is not my cup of tea, I prefer C, but some people swear by C++.)
Also keep in mind, that C++ uses a runtime that pretty much demands an operating system, for exception support.
If you envision your library to be used as a foundation for an operating system, or to be used in environments without an operating system you either have to know how to disable exception support, avoiding a lot (all?) of STL and provide your own allocator and deallocator. It's not impossible, but you need to know exactly what you do.
C is more suited to those low level kinds of things.