从 C 优雅地调用 C++
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7281441/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Elegantly call C++ from C
提问by Cartesius00
We develop some project in plain C
(C99). But, we have one library as source codes (math library) in C++
. We need this library so I would like to ask, what is the most elegant way to integrate this source codes?
我们用普通的C
(C99)开发了一些项目。但是,我们有一个库作为源代码(数学库)在C++
. 我们需要这个库所以我想问一下,集成这个源代码的最优雅的方式是什么?
Ratio between sizes of C
and C++
is 20:1
so moving to C++
is not the option. Should we use static library? DLL? (It's all on Windows).
C
和的大小之间的比率C++
是20:1
如此移动到C++
不是选项。我们应该使用静态库吗?动态链接库?(这一切都在 Windows 上)。
回答by Marcelo Cantos
EDIT:Based on discussion in the comment, I should point out that separating things into a C-compatible struct duck
and a derived class Duck
is probably unnecessary. You can probably safely shovel the implementation into struct duck
and eliminate class Duck
, thus obviating real(…)
. But I don't know C++ well enough (in particular, the way it interacts with the C universe) to offer a definitive answer on this.
编辑:根据评论中的讨论,我应该指出将事物分为 C 兼容struct duck
和派生class Duck
的可能是不必要的。您可能可以安全地将实现推入struct duck
并消除class Duck
,从而避免real(…)
. 但我对 C++ 的了解不够(特别是它与 C 宇宙交互的方式),无法对此提供明确的答案。
There is no reason you can't simply link all your C and C++ code together into a single binary.
没有理由不能简单地将所有 C 和 C++ 代码链接到一个二进制文件中。
Interfacing to the C++ code requires that you wrap the C++ API in a C API. You can do this by declaring a bunch of functions inside extern "C" { ... }
when compiling the C++ code, and without the extern declaration when compiling the C client code. E.g.:
与 C++ 代码的接口要求您将 C++ API 包装在 C API 中。您可以通过extern "C" { ... }
在编译 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
You can define the duck struct in your C++ source, and even inherit the real Duck
class from it:
你可以在你的 C++ 源代码中定义duck 结构,甚至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); }
回答by A.Robert
The only reason to want to inherit from the duck struct would be to expose some to its attributes in the C API, which is generally considered bad style anyway. Without inheritance, your C header would look like this:
想要从duck 结构继承的唯一原因是在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);
And this would be the corresponding implementation, with no need for type casts:
这将是相应的实现,不需要类型转换:
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); }
In the same way, a C API can be created for a C++ interface (pure virtual class) and its implementations. In that case, only the constructor need to be based on the concrete implementation (e.g. new_RubberDuck(2)). The destructor and all other functions will automatically operate on the correct implementation, same as in C++.
同样,可以为 C++ 接口(纯虚拟类)及其实现创建 C API。在这种情况下,只有构造函数需要基于具体实现(例如 new_RubberDuck(2))。析构函数和所有其他函数将自动在正确的实现上运行,就像在 C++ 中一样。
回答by A. Robert
A C++ math library may well be implemented in the for of utility classes (static members only). In this case, a much simpler approach could be taken:
C++ 数学库很可能在实用程序类的 for 中实现(仅限静态成员)。在这种情况下,可以采用更简单的方法:
class FPMath {
public:
static double add(double, double);
static double sub(double, double);
static double mul(double, double);
static double div(double, double);
};
The header for the C interface would then be:
C 接口的标头将是:
double FPMath_add(double, double);
double FPMath_sub(double, double);
double FPMath_mul(double, double);
double FPMath_div(double, double);
And the corresponding implementation might be:
相应的实现可能是:
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); }
But maybe this is stating the obvious....
但也许这说明了显而易见的......
回答by Some programmer dude
There isa way to create a "hack" that allows you to call member functions of some objects directly.
这里是一个方法来创建一个“黑客”,可以让你直接调用某些对象的成员函数。
The first thing you have to do is to create an extern "C"
factory function, which returns a pointer (as void*
) to the object.
你要做的第一件事是创建一个extern "C"
工厂函数,它返回一个指向void*
对象的指针(as )。
The second thing you need is the mangled name of the member function.
您需要的第二件事是成员函数的重整名称。
Then you can call the function using the mangled name, and passing the pointer returned from the factory function as the first argument.
然后您可以使用重整名称调用该函数,并将从工厂函数返回的指针作为第一个参数传递。
Caveats:
注意事项:
- Will of course not work calling member function that wants other objects, or references, or other C++ stuff, or functions returning objects or types not compatible with C types
- Will not work on virtual member functions, and probably not on objects with virtual functions in them even if it's not a virtual function being called
- The mangled name have to be a valid C symbol
- Any many many more...
- 调用需要其他对象、引用或其他 C++ 内容的成员函数,或者返回与 C 类型不兼容的对象或类型的函数,当然不起作用
- 不适用于虚成员函数,并且可能不适用于其中包含虚函数的对象,即使它不是被调用的虚函数
- 损坏的名称必须是有效的 C 符号
- 还有更多...
This is not something I recommend, quite the opposite in fact. I strongly advise against doing something like outlined in this answer. It's unsupported and probably undefined behavior and may break in weird and unpredictable ways.
这不是我推荐的,事实上恰恰相反。我强烈建议不要做这个答案中概述的事情。它不受支持,可能是未定义的行为,并且可能会以奇怪和不可预测的方式中断。