为 C 消费包装 C++ 类 API
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1588788/
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
Wrapping C++ class API for C consumption
提问by Exponent
I have a set of related C++ classes which must be wrapped and exported from a DLL in such a way that it can be easily consumed by C / FFI libraries. I'm looking for some "best practices" for doing this. For example, how to create and free objects, how to handle base classes, alternative solutions, etc...
我有一组相关的 C++ 类,它们必须以一种可以被 C/FFI 库轻松使用的方式从 DLL 中包装和导出。我正在寻找一些“最佳实践”来做到这一点。例如,如何创建和释放对象,如何处理基类,替代解决方案等......
Some basic guidelines I have so far is to convert methods into simple functions with an extra void* argument representing the 'this' pointer, including any destructors. Constructors can retain their original argument list, but must return a pointer representing the object. All memory should be handled via the same set of process-wide allocation and free routines, and should be hot-swappable in a sense, either via macros or otherwise.
到目前为止,我拥有的一些基本准则是将方法转换为简单的函数,并使用一个额外的 void* 参数来表示“this”指针,包括任何析构函数。构造函数可以保留其原始参数列表,但必须返回一个表示对象的指针。所有内存都应该通过同一套进程范围的分配和空闲例程来处理,并且在某种意义上应该是可热插拔的,无论是通过宏还是其他方式。
采纳答案by Martin York
Foreach public method you need a C function.
You also need an opaque pointer to represent your class in the C code.
It is simpler to just use a void* though you could build a struct that contains a void* and other information (For example if you wanted to support arrays?).
Foreach 公共方法需要一个 C 函数。
您还需要一个不透明的指针来在 C 代码中表示您的类。
尽管您可以构建一个包含 void* 和其他信息的结构(例如,如果您想支持数组?),但仅使用 void* 更简单。
Fred.h
--------------------------------
#ifdef __cplusplus
class Fred
{
public:
Fred(int x,int y);
int doStuff(int p);
};
#endif
//
// C Interface.
typedef void* CFred;
//
// Need an explicit constructor and destructor.
extern "C" CFred newCFred(int x,int y);
extern "C" void delCFred(CFred);
//
// Each public method. Takes an opaque reference to the object
// that was returned from the above constructor plus the methods parameters.
extern "C" int doStuffCFred(CFred,int p);
The the implementation is trivial.
Convert the opaque pointer to a Fred and then call the method.
实现是微不足道的。
将不透明指针转换为 Fred,然后调用该方法。
CFred.cpp
--------------------------------
// Functions implemented in a cpp file.
// But note that they were declared above as extern "C" this gives them
// C linkage and thus are available from a C lib.
CFred newCFred(int x,int y)
{
return reinterpret_cast<void*>(new Fred(x,y));
}
void delCFred(CFred fred)
{
delete reinterpret_cast<Fred*>(fred);
}
int doStuffCFred(CFred fred,int p)
{
return reinterpret_cast<Fred*>(fred)->doStuff(p);
}
回答by codingFriend1
While Loki Astari's answer is very good, his sample code puts the wrapping code inside the C++ class. I prefer to have the wrapping code in a separate file. Also I think it is better style to prefix the wrapping C functions with the class name.
虽然 Loki Astari 的回答非常好,但他的示例代码将包装代码放在 C++ 类中。我更喜欢将包装代码放在一个单独的文件中。此外,我认为用类名作为包装 C 函数的前缀是更好的风格。
The following blog posts shows how to do that: http://blog.eikke.com/index.php/ikke/2005/11/03/using_c_classes_in_c.html
以下博客文章展示了如何做到这一点:http: //blog.eikke.com/index.php/ikke/2005/11/03/using_c_classes_in_c.html
I copied the essential part because the blog is abandoned and might finally vanish (credit to Ikke's Blog):
我复制了重要部分,因为该博客已被放弃并且可能最终会消失(归功于 Ikke 的博客):
First we need a C++ class, using one header file (Test.hh)
首先我们需要一个 C++ 类,使用一个头文件(Test.hh)
class Test {
public:
void testfunc();
Test(int i);
private:
int testint;
};
and one implementation file (Test.cc)
和一个实现文件(Test.cc)
#include <iostream>
#include "Test.hh"
using namespace std;
Test::Test(int i) {
this->testint = i;
}
void Test::testfunc() {
cout << "test " << this->testint << endl;
}
This is just basic C++ code.
这只是基本的 C++ 代码。
Then we need some glue code. This code is something in-between C and C++. Again, we got one header file (TestWrapper.h, just .h as it doesn't contain any C++ code)
然后我们需要一些胶水代码。这段代码介于 C 和 C++ 之间。同样,我们得到了一个头文件(TestWrapper.h,只是 .h,因为它不包含任何 C++ 代码)
typedef void CTest;
#ifdef __cplusplus
extern "C" {
#endif
CTest * test_new(int i);
void test_testfunc(const CTest *t);
void test_delete(CTest *t);
#ifdef __cplusplus
}
#endif
and the function implementations (TestWrapper.cc, .cc as it contains C++ code):
和函数实现(TestWrapper.cc,.cc 因为它包含 C++ 代码):
#include "TestWrapper.h"
#include "Test.hh"
extern "C" {
CTest * test_new(int i) {
Test *t = new Test(i);
return (CTest *)t;
}
void test_testfunc(const CTest *test) {
Test *t = (Test *)test;
t->testfunc();
}
void test_delete(CTest *test) {
Test *t = (Test *)test;
delete t;
}
}
回答by C?t?lin Piti?
First, you might not need to convert all your methods to C functions. If you can simplify the API and hide some of the C++ interface, it is better, since you minimize the chance to change the C API when you change C++ logic behind.
首先,您可能不需要将所有方法都转换为 C 函数。如果您可以简化 API 并隐藏一些 C++ 接口,那就更好了,因为当您更改 C++ 逻辑时,您可以最大限度地减少更改 C API 的机会。
So think of a higher level abstraction to be provided through that API. Use that void* solution you described. It looks to me the most appropriate (or typedef void* as HANDLE :) ).
因此,请考虑通过该 API 提供更高级别的抽象。使用您描述的 void* 解决方案。它在我看来是最合适的(或 typedef void* 作为 HANDLE :))。
回答by elder_george
Some opinions from my experience:
以我的经验提出几点意见:
- functions should return codes to represent errors. It's useful to have a function returning error description in string form. All other return values should be out parameters.
- 函数应该返回代码来表示错误。让函数以字符串形式返回错误描述很有用。所有其他返回值都应该是 out 参数。
E.g.:
例如:
C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget);
- put signatures into structures/classes your handles pointer to for checking handles on validness.
- 将签名放入您的句柄指针指向的结构/类中,以检查句柄的有效性。
E.g. your function should look like:
例如,您的函数应如下所示:
C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget){
Ui* ui = (Ui*)ui;
if(ui.Signature != 1234)
return BAD_HUI;
}
- objects should be created and released using functions exported from DLL, since memory allocation method in DLL and consuming app can differ.
- 应使用从 DLL 导出的函数创建和释放对象,因为 DLL 和消费应用程序中的内存分配方法可能不同。
E.g.:
例如:
C_ERROR CreateUi(HUI* ui);
C_ERROR CloseUi(HUI hui); // usually error codes don't matter here, so may use void
- if you are allocating memory for some buffer or other data that may be required to persist outside of your library, provide size of this buffer/data. This way users can save it to disk, DB or wherever they want without hacking into your internals to find out actual size. Otherwise you'll eventually need to provide your own file I/O api which users will use only to convert your data to byte array of known size.
- 如果您正在为某些缓冲区或可能需要在库外持久保存的其他数据分配内存,请提供此缓冲区/数据的大小。通过这种方式,用户可以将其保存到磁盘、数据库或任何他们想要的地方,而无需侵入您的内部结构以找出实际大小。否则,您最终将需要提供您自己的文件 I/O api,用户将仅使用它来将您的数据转换为已知大小的字节数组。
E.g.:
例如:
C_ERROR CreateBitmap(HUI* ui, SIZE size, char** pBmpBuffer, int* pSize);
- if your objects has some typical representation outside of your C++ library, provide a mean of converting to this representation (e.g. if you have some class
Image
and provide access to it viaHIMG
handle, provide functions to convert it to and from e.g. windows HBITMAP). This will simplify integration with existing API.
- 如果你的对象在你的 C++ 库之外有一些典型的表示,提供一种转换到这种表示的方法(例如,如果你有一些类
Image
并通过HIMG
句柄提供对它的访问,提供将它转换为和从 Windows HBITMAP 转换的函数)。这将简化与现有 API 的集成。
E.g.
例如
C_ERROR BitmapToHBITMAP(HUI* ui, char* bmpBuffer, int size, HBITMAP* phBmp);
回答by Daniel Daranas
Use vector (and string::c_str) to exchange data with non C++ APIs. (Guideline #78 from C++ Coding Standards, H. Sutter/ A. Alexandrescu).
使用 vector(和 string::c_str)与非 C++ API 交换数据。(来自C++ 编码标准的指南 #78 ,H. Sutter/A. Alexandrescu)。
PS It's not that true that "constructors can retain their original argument list". This is only true for argument types which are C-compatible.
PS “构造函数可以保留其原始参数列表”并非如此。这仅适用于 C 兼容的参数类型。
PS2 Of course, listen to C?t?linand keep your interface as small and simple as possible.
PS2 当然,听听C?t?lin并让您的界面尽可能小而简单。
回答by Doug T.
This may be of interest: "Mixing C and C++"at the C++ FAQ Lite. Specifically [32.8] How can I pass an object of a C++ class to/from a C function?
这可能很有趣:C++ FAQ Lite 中的“Mixing C and C++”。具体来说[32.8] 如何将 C++ 类的对象传递给/从 C 函数传递?