为 C 消费包装 C 类 API

新手上路,请多包涵

我有一组相关的 C++ 类,它们必须从 DLL 包装和导出,以便 C/FFI 库可以轻松使用它。我正在寻找一些“最佳实践”来做到这一点。例如,如何创建和释放对象、如何处理基类、替代解决方案等……

到目前为止,我的一些基本准则是将方法转换为简单的函数,其中包含一个表示“this”指针的额外 void* 参数,包括任何析构函数。构造函数可以保留其原始参数列表,但必须返回一个表示对象的指针。所有内存都应该通过同一组进程范围的分配和空闲例程来处理,并且在某种意义上应该是可热交换的,无论是通过宏还是其他方式。

原文由 Exponent 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 400
2 个回答

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

实现是微不足道的。

将不透明指针转换为 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);
}

原文由 Martin York 发布,翻译遵循 CC BY-SA 2.5 许可协议

根据我的经验的一些意见:

  • 函数应该返回代码来表示错误。以字符串形式返回错误描述的函数很有用。所有其他返回值都应该是 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);

原文由 elder_george 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题