使用C和C ++接口编写库,以哪种方式包装?

时间:2010-10-12 07:00:21

标签: c++ c library-design

在准备一个库(让我们称之为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作为一种语言,因为我觉得它是一个较小的语义雷区。别人怎么想?

编辑:这么多好的答案。谢谢大家。遗憾的是我只能接受一个。

9 个答案:

答案 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