如何在 C 中创建用于使用 C++ 代码的包装器?

How to create wrapper for using C++ code in C?

本文关键字:代码 C++ 包装 用于 创建      更新时间:2023-10-16

我正在尝试为C++第三方库做一个 C 包装器,因为我需要在 C 项目中使用它。 我见过C++类包装器的示例,但我没有理解该过程,也无法包装C++结构。

我想包装的结构:

struct confGlobal{
long int device;
string name;
int number;
string uid;
string message;
bool mode;
confiGlobal():device{LONG_MAX}, number{INT_MAX}{}
};
struct product{
string id;
string name;
};
struct category{
unsigned char id;
string name;
string description;
category():id{UCHAR_MAX}{}
};
struct subCategory{
unsigned char id;
string name;
string description;
unsigned char idRoot;
subCategory():id{UCHAR_MAX}, idRoot{UCHAR_MAX}{}
};
struct confPartner{
vector<struct product> tableProduct;
vector<struct category> tableCategory;
vector<struct subCategory> tableSubCategory;
};

对于对此方法的调用:

class my_API {
public:
static my_API* Instance(struct confGlobal cGlobal,
struct confPartner cPartner);
... another methods ...
private:
virtual ~my_API();
struct confGlobal cGlobal;
struct confPertner cPartner;
};

我需要填写这个结构并从 C 调用my_API::Instance(),但我的尝试没有成功。

包装器.h

#ifdef __cplusplus
extern "C" {
#endif
struct confGlobal_wpr; // An opaque type that we'll use as a handle
typedef struct confGlobal_wpr confGlobal_wpr;
confGlobal_wpr *confGlobal_create(unsigned int device,
char *name,
int number,
char *uid,
char *message,
unsigned char mode);
struct product_wpr{
char id[4];
char name[30];
};
typedef struct product_wpr product_wpr;
struct category_wpr{
unsigned char id;
char name[3];
char description[30];
};
typedef struct category_wpr category_wpr;
struct subcategory_wpr{
unsigned char id;
char name[3];
char description[30];
unsigned char idRoot;
};
typedef struct subCategory_wpr subCategory_wpr;
struct confPartner_wpr; // An opaque type that we'll use as a handle
typedef struct confPartner_wpr confPartner_wpr;
confPartner_wpr *confPartner_create(Product_wpr tableProducts[],
unsigned char numProducts,
Category_wpr tableCategories[],
unsigned char numCategories,
SubCategory_wpr tableSubCategories[],
unsigned char numSubCategories);
struct my_api_wpr;
typedef struct my_api_wpr my_api_wpr;
my_api_wpr *my_api_create(struct confGlobal_wpr cGlobal,
struct confPartner_wpr cPartner);
#ifdef __cplusplus
}
#endif

包装纸.cpp

confGlobal_wpr *confGlobal_create(unsigned int device,
char *name,
int number,
char *uid,
char *message,
unsigned char mode)
{
confGlobal_wpr *cg;
struct confGlobal confiGlobal;
confiGlobal.name = name;
confiGlobal.device = device;
confiGlobal.number = number;
confiGlobal.uid = uid;
confiGlobal.message = message;
if (mode == 0)
confiGlobal.mode = false;
else
confiGlobal.mode = true;
return cg;
}
void confGlobal_destroy(confGlobal_wpr *cg)
{
if (cg == NULL)
return;
delete static_cast<confGlobal_wpr *>(cg->instance); // ERROR: invalid static_cast from type ‘confGlobal’ to type ‘confGlobal_wpr*’
free(cg);
}
confPartner_wpr *confPartner_create(product_wpr tableProducts_wpr[],
unsigned char numProducts,
category_wpr tableCategories_wpr[],
unsigned char numCategories,
subCategory_wpr tableSubCategories_wpr[], 
unsigned char numSubCategories)
{
unsigned char i=0;
confPartner_wpr *cc;
struct confPartner cPartner;
vector< struct product> tableProduct;
vector< struct category> tableCategory;
vector< struct subCategory> tableSubCategory;
for (i=0; i<numProducts; i++)
{
struct product p;
p.id = tableProducts_wpr[i].id;
p.name = tableProducts_wpr[i].name;
tableProduct.push_back(p);
}
cPartner.tableProduct = tableProducts;
for (i=0; i<numCategories; i++)
{       
struct category c;
c.id = tableCategories_wpr[i].id;
c.nombre = tableCategories_wpr[i].name;
c.descripcion = tableCategories_wpr[i].description;
tableCategory.push_back(c);
}
cPartner.tableCategory = tableCategory;
for (i=0; i<numSubCategories; i++)
{
struct subZona sc;
sc.id = tableSubCategories_wpr[i].id;
sc.name = tableSubCategories_wpr[i].name;
sc.description = tableSubCategories_wpr[i].description;
sc.idRoot = tableSubCategories_wpr[i].idRoot;
tableSubCategory.push_back(sc);     
}
cPartner.tableSubCategory = tableSubCategory;
return cc;
}
my_api_wpr *my_api_create(struct confGlobal_wpr confiGlobal_wpr,
struct confPartner_wpr confiPartner_wpr)
{
my_api_wpr *my_api;
my_API *obj;
my_api = (typeof(my_api))malloc(sizeof(*my_api));
obj    = my_API::Instance(confiGlobal_wpr, confiConsorcio_wpr);
/* With this compile and linked OK
confGlobal cg;
confPartner cc;
obj    = my_API::Instance(cg, cc);
*/
my_api->obj = obj;
return my_api;
}
void my_api_destroy(ct_api_wpr *my_api)
{
if (my_api == NULL)
return;
delete static_cast<my_API *>(my_api->ptr_api); // ERROR: ‘virtual my_API::~my_API()’ is private within this context
free(my_api);
}

编译和链接时的输出错误:

g++ -shared -o libwrapper.so *.cpp wrapper.h -l:libthird-party.a -L. -ldl -lrt -Wl,-rpath /usr/local/lib -lc
In function ‘my_api_wpr* my_api_create(confGlobal_wpr, confPartner_wpr)’:
error: no matching function for call to ‘my_API::Instance(confGlobal_wpr&, confPartner_wpr&)’
obj    = my_API::Instance(confiGlobal_wpr, confiConsorcio_wpr);
^
my_API.h:30:20: note: candidate: static my_API* my_API::Instance(confGlobal, confPartner)
static my_API* Instance(struct confGlobal cGlobal, struct confiPartner cPartner);
^~~~~~~~
my_API.h:30:20: note:   no known conversion for argument 1 from ‘confGlobal_wpr’ to ‘confGlobal’

你忘记了CT_API::Instance不理解你在 C 中创建的用于包装C++结构的"句柄"类型。 如果您阅读错误消息,这正是错误消息告诉您的内容。 您必须将它们转换回适当的C++类型。

首先,由于您使用"create"样式的例程来构建结构并将它们作为指针返回,因此应考虑使my_api_create函数接受指针。 特别是因为生成的句柄类型是前向声明的结构,在 C 中没有可见的定义,并且 C 客户端无法取消引用它们。

这突出了另一个问题。您也没有从C++正确使用这些句柄。

所以,一次一件事...

您在 C 中的创建例程应声明为:

my_api_wpr *my_api_create(struct confGlobal_wpr* cGlobal, struct confPartner_wpr* cPartner);

在C++方面,您需要实际定义句柄类型。 像这样:

extern "C" struct confGlobal_wpr {
struct confGlobal instance;
};
extern "C" struct confPartner_wpr {
struct confPartner instance;
};
extern "C" struct my_api_wpr {
my_API *ptr;
};

现在,您的创作:

confGlobal_wpr *confGlobal_create(unsigned int device,
char *name,
int number,
char *uid,
char *message,
unsigned char mode)
{
confGlobal_wpr *cg = new confGlobal_wpr;
struct confGlobal& cGlobal = cg->instance; //<-- note the reference
// TODO: populate cGlobal as usual
return cg;
}
confPartner_wpr *confPartner_create(product_wpr tableProducts_wpr[],
unsigned char numProducts,
category_wpr tableCategories_wpr[],
unsigned char numCategories,
subCategory_wpr tableSubCategories_wpr[],
unsigned char numSubCategories)
{
confPartner_wpr *cc = new confPartner_wpr;
struct confPartner& cPartner = cc->instance; //<-- note the reference
// TODO: populate cPartner as usual
return cc;
}
my_api_wpr *my_api_create(struct confGlobal_wpr* cGlobal, struct confPartner_wpr* cPartner)
{
my_api_wpr *my_api = new my_api_wpr;
my_api->ptr = CT_API::Instance(cGlobal->instance, cPartner->instance);
return my_api;
}

您还应该为上述所有内容添加相应的_destroy方法。

要在 C 项目中使用C++代码,您需要使用 C 调用约定定义包装函数 - extern "C"(关闭C++名称重整/装饰),并在 C 项目中调用它们并且仅调用它们。在这些 C 函数中,您可以使用C++代码。仅传递给 C 包装器函数 C 理解的类型。您可以创建中间结构来将数据传递到 C 包装器函数。然后,您需要将数据复制到类C++期望的类型。在特定情况下,您错误地传递了包装结构confGlobal_wpr但C++方法需要confGlobal,编译器直接对此提出抱怨。

下面是如何使用 C 代码中的C++代码的可观察片段: 福.h

#include <string>
class Bar
{
public:
Bar(std::string s) : s_(s)
{
}
std::string s_;
};
class Foo
{
public:
Foo(Bar) {}
};

CWrappers.h//将此标头包含在 C 项目中

struct BarWrapper
{
char data[100] = "";
};
#ifdef __cplusplus
extern "C" {
#endif
BarWrapper createFoo(char *c);
#ifdef __cplusplus
}
#endif

包装器.cpp

#include <algorithm>
#include "Foo.h"
#include "CWrappers.h"
// input and output to this C function should be understandable for C
BarWrapper createFoo(char *c)
{
// inside you can use C++
std::string data(c);
Bar bar(data);
Foo * foo = new Foo(bar);
BarWrapper barWrapper;
std::copy(data.begin(), data.end(), barWrapper.data);
return barWrapper; // pack data to C struct wrapper (that has no C++ specific)
}

C 项目

#include "CWrappers.h"
int main()
{
BarWrapper barWrapper = createFoo((char *)"test");
}

包含除 C 和 C++ 通用的基元数据类型以外的任何内容的结构在很大程度上不能以您想要的方式包装。在这种特殊情况下,您的结构中有::std::strings。而且它们绝对无法从 C 合理地访问。

此外,结构包含bool,我不知道较新版本的 C 标准是否以这样一种方式bool或定义它,以便在同一平台上产生与C++实现的布局兼容结构。

这个问题有解决方案。但它们涉及使用指向 C 中结构的不透明指针,并始终调用函数来访问其方法。我将尝试举一个例子来说明它如何适用于一个非常简单的结构。

更仔细地查看您的代码,看起来您需要一种 thunk 层,该层采用 C 结构并(在 C++ 中)手动将其转换为 C++ 结构,并返回指向动态分配的C++结构的指针,然后您可以将其传递给已公开给 C 的其他C++函数。