从C中优雅地调用C ++
我们开发一些简单的C
(C99)项目。 但是,我们在C++
有一个作为源代码(math库)的库。 我们需要这个库,所以我想问一下,整合这个源代码的最优雅的方法是什么?
C
和C++
大小之间的比例是20:1
因此移到C++
不是选项。 我们应该使用静态库吗? DLL? (这一切都在Windows上)。
编辑:根据评论中的讨论,我应该指出,将东西分解成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 ++源代码中定义鸭结构,甚至可以从中inheritance真实的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); }
想要从鸭结构inheritance的唯一原因是将C API中的一些属性暴露出来,无论如何这通常被认为是不好的风格。 没有inheritance,你的C头看起来像这样:
struct Duck; struct Duck* new_Duck(int feet); void delete_Duck(struct Duck* d); void Duck_quack(struct Duck* d, float volume);
这将是相应的实现,不需要types转换:
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 ++接口(纯虚拟类)及其实现创build一个C API。 在这种情况下,只有构造函数需要基于具体的实现(例如new_RubberDuck(2))。 析构函数和所有其他函数将自动在正确的实现上运行,就像在C ++中一样。
C ++math库很可能在实用程序类(仅限静态成员)中实现。 在这种情况下,可以采取更简单的方法:
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); }
但也许这是明显的….
有一种方法可以创build一个“hack”,允许你直接调用一些对象的成员函数。
你必须做的第一件事是创build一个extern "C"
工厂函数,它返回一个指针( void*
)给对象。
你需要的第二件事是成员函数的错位名称。
然后,你可以使用mangled名称来调用该函数,并将工厂函数返回的指针作为第一个parameter passing。
注意事项:
- 当然不会调用成员函数,它需要其他对象,引用,或其他C ++的东西,或返回对象或types与Ctypes不兼容的函数
- 不会在虚拟成员函数上工作,也可能不在虚拟函数中的对象上,即使它不是被调用的虚函数
- 重名的名称必须是有效的C符号
- 任何许多更多…
这不是我推荐的,事实上恰恰相反。 我强烈build议不要做这样的回答。 这是不受支持的,可能是未定义的行为,并可能以怪异和不可预知的方式打破。