包裹C ++类的API对C消费(Wrapping C++ class API for C consu

2019-08-20 10:25发布

我有一组相关的C ++类必须被包裹并以这样的方式,它可以通过C / FFI文库中容易地消耗从DLL导出的。 我在寻找做一些这方面的“最佳实践”。 例如,如何创建和免费的对象,如何处理基类,可选择的解决方案,等等。

一些基本准则,我至今是方法转化为简单的功能转换与代表this指针,包括任何析构一个额外的void *的说法。 构造函数可以保留原来的参数列表,但必须返回表示对象的指针。 所有的内存应该通过相同的一组进程级的分配和释放例程,无论是通过宏或以其他方式进行处理,而应该是支持热插拔的意义。

Answer 1:

的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);

的实现很简单。
不透明指针转换为弗雷德,然后调用方法。

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);
}


Answer 2:

虽然洛基阿斯塔的回答非常好,他的样本代码把C ++类里面的封装代码。 我希望有一个单独的文件的封装代码。 此外,我认为这是更好的风格前缀包装的C函数的类名。

下面的博客文章介绍如何做到这一点: http://blog.eikke.com/index.php/ikke/2005/11/03/using_c_classes_in_c.html

我复制的重要组成部分,因为博客是被遗弃的,可能最终消失(信贷IKKE的博客):


首先,我们需要一个C ++类,使用一个头文件(Test.hh)

class Test {
    public:
        void testfunc();
        Test(int i);

    private:
        int testint;
};

和一个实现文件(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;
}

这仅仅是基本的C ++代码。

然后,我们需要一些胶水代码。 此代码是在两者之间的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

和功能实现(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;
    }
}


Answer 3:

首先,你可能不需要所有的方法转换为C函数。 如果你能简化API并隐藏了一些C ++接口,它是更好,因为你尽量减少改变C API,当你改变落后C ++逻辑的机会。

因此,认为更高的抽象层次上通过API提供。 用你的描述void *的解决方案。 在我看来最合适的(或无效的typedef *为HANDLE :))。



Answer 4:

从我的经验几点看法:

  • 函数应该返回代码来表示错误。 这是非常有用的功能以字符串形式返回错误的描述。 所有其他的返回值应该是out参数。

例如:

C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget);
  • 把签名到结构/类的句柄指针上区分有效性检查手柄。

例如,你的函数应该是这样的:

C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget){
    Ui* ui = (Ui*)ui;
    if(ui.Signature != 1234)
    return BAD_HUI;
}
  • 对象应该被创建并使用来自DLL导出函数释放,因为在DLL存储器分配方法和消耗的应用可以是不同的。

例如:

C_ERROR CreateUi(HUI* ui);
C_ERROR CloseUi(HUI hui); // usually error codes don't matter here, so may use void
  • 如果您分配了一定的缓冲或可能需要的,以保存库之外的其他数据存储,提供该缓冲/数据的大小。 这样,用户就可以将其保存到磁盘,数据库或任何他们想要的没有黑客进入你的内部,找出实际大小。 否则,你最终会需要提供自己的文件,该文件的用户将只能使用你的数据转换为已知大小的字节数组I / O API。

例如:

C_ERROR CreateBitmap(HUI* ui, SIZE size, char** pBmpBuffer, int* pSize);
  • 如果你的对象有你的C ++库之外的一些典型代表,提供转换为这种表示的平均值(例如,如果你有一些类Image ,并提供通过访问它HIMG手柄,提供的功能将其转换为从如Windows HBITMAP) 。 这将简化与现有的API集成。

C_ERROR BitmapToHBITMAP(HUI* ui, char* bmpBuffer, int size, HBITMAP* phBmp);


Answer 5:

使用载体(和串:: c_str)与非C ++的API交换数据。 (准则#78从C ++编码标准 ,H.萨特/ A. Alexandrescu的)。

PS这不是真的,“构造函数可以保留其原有的参数列表”。 这仅仅是参数类型它们是C兼容真。

PS2当然,听白内停 ,并保持你的界面小而简单越好。



Answer 6:

这可能是兴趣: “混合C和C ++”在C ++ FAQ精简版。 具体地说[32.8]如何可以从C函数通过C ++类的一个目的是/?



文章来源: Wrapping C++ class API for C consumption