2011-09-02 37 views
44

我们开发了一些简单的项目C(C99)。但是,我们在C++中有一个作为源代码的库(数学库)。我们需要这个库,所以我想问一下,整合这个源代码最优雅的方式是什么?从C中优雅地调用C++

尺寸为CC++之间的比率为20:1因此不能选择C++。我们应该使用静态库吗? DLL? (全部在Windows上)。

+8

C++库是否提供了一个合适的C接口(自由函数而不是成员函数,没有例外,extern“C”等)? –

+0

混合C和C++的常见问题解答:http://www.parashift.com/c++-faq/mixing-c-and-cpp.html – Maciej

+1

[如何从C调用C++函数](http:// stackoverflow.com/questions/2744181/how-to-call-c-function-from-c) –

回答

50

编辑:基于在评论的讨论,我要指出的是分离的东西到C兼容struct duck和派生class Duck可能是不必要的。您可能可以安全地将执行铲入struct duck并消除class Duck,从而避免real(…)。但是我不太了解C++(尤其是它与C宇宙交互的方式),以便为此提供明确的答案。


没有理由你不能简单地链接所有的C和C++代码一起到一个单一的二进制文件。

与C++代码的接口需要将C++ API封装到C API中。你可以通过在编译C++代码时在extern "C" { ... }内声明一堆函数,并且在编译C客户端代码时不用extern声明来实现。例如:

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct duck duck; 

duck* new_duck(int feet); 
void delete_duck(duck* d); 
void duck_quack(duck* d, float volume); 

#ifdef __cplusplus 
} 
#endif 

你可以在你的C++源定义鸭结构,甚至从它继承了真正的Duck类:

struct duck { }; 

class Duck : public duck { 
public: 
    Duck(int feet); 
    ~Duck(); 

    void quack(float volume); 
}; 

inline Duck* real(duck* d) { return static_cast<Duck*>(d); } 

duck* new_duck(int feet) { return new Duck(feet); } 
void delete_duck(duck* d) { delete real(d); } 
void duck_quack(duck* d, float volume) { real(d)->quack(volume); } 
+0

很好的答案,谢谢! – Cartesius00

+0

国际海事组织空白指针会更适合在这里 – Ulterior

+32

@Ulterior:不IMO。如果你把鸭子和青蛙当成空针,你可能会不小心让青蛙嘎嘎作响,这会撕裂宇宙的结构。 –

7

的唯一原因想从鸭结构继承会在C API中公开一些属性,无论如何这通常被认为是不好的风格。没有继承,你的C头应该是这样的:

struct Duck; 

struct Duck* new_Duck(int feet); 
void delete_Duck(struct Duck* d); 
void Duck_quack(struct Duck* d, float volume); 

,这将是相应的实施,而无需类型转换:

extern "C" { 
#include "Duck.h" 
} 

class Duck { 
public: 
    Duck(int feet) : {} 
    ~Duck() {} 

    void quack(float volume) {} 
}; 

struct Duck* new_Duck(int feet) { return new Duck(feet); } 
void delete_Duck(struct Duck* d) { delete d; } 
void Duck_quack(struct Duck* d, float volume) { d->quack(volume); } 

以同样的方式,一个C API可为C++接口(纯虚拟类)及其实现创建。在这种情况下,只有构造函数需要基于具体实现(例如new_RubberDuck(2))。析构函数和所有其他函数将自动在正确的实现上运行,与C++中的一样。

+2

函数实现也应该被标记为extern“C”。否则,你会得到链接错误,因为“new_Duck :: i”或者你的平台上的任何名字使得C++函数与头文件的“new_Duck”不匹配。 – uliwitness

5

C++数学库很可能在实用程序类(仅限静态成员)中实现。在这种情况下,一个更简单的方法可以采取:

class FPMath { 
public: 
    static double add(double, double); 
    static double sub(double, double); 
    static double mul(double, double); 
    static double div(double, double); 
}; 

针对C接口的头然后将:

double FPMath_add(double, double); 
double FPMath_sub(double, double); 
double FPMath_mul(double, double); 
double FPMath_div(double, double); 

和相应的实现可能是:

double FPMath_add(double a, double b) { return FPMath::add(a, b); } 
double FPMath_sub(double a, double b) { return FPMath::sub(a, b); } 
double FPMath_mul(double a, double b) { return FPMath::mul(a, b); } 
double FPMath_div(double a, double b) { return FPMath::div(a, b); } 

但也许这是明显的....

0

There is一种创建“黑客”的方法,允许您直接调用某些对象的成员函数。

您需要做的第一件事是创建一个extern "C"工厂函数,该函数返回一个指针(如void*)到对象。

您需要的第二件事是成员函数的损坏名称。

然后,您可以使用mangled名称并将工厂函数返回的指针作为第一个参数传递给该函数。

注意事项:

当然
  • 将无法​​正常工作调用想要其他物体,或引用,或其他C++的东西,或返回对象或类型的功能与C类型不兼容的成员函数
  • 将无法​​正常工作虚拟成员函数,并且可能不会对他们有虚函数的对象,即使它没有被调用
  • 错位的名称必须是一个有效的C符号
  • 任何许多许多......
  • 虚函数

这不是我推荐的,事实上恰恰相反。我强烈建议不要执行此答案中概述的内容。它不受支持,可能未定义行为,并可能以怪异和不可预知的方式打破。