我正在尝试将现有的第三方 C++库封装到C接口,以便它可以用于另一种语言的绑定。我很难弄清楚如何包装一个名称空间枚举,而不是仅仅重新定义它:
// Existing C++ 3rd party library header
namespace foo {
enum Fruit {
APPLE = 0,
ORANGE
}
}
所以我用一个extern "C"
块包装了我的.{h,cpp},我只是不知道如何将foo::Fruit
枚举导出到C接口中。
// wrapped.h
#ifdef __cplusplus
extern "C" {
#endif
// I don't want to do this
typedef enum Fruit {
APPLE = 0,
ORANGE
} Fruit;
#ifdef __cplusplus
}
#endif
#endif
是否可以将(镜像) foo::Fruit
从C++库导出到我的C包装器中作为Fruit
发布于 2013-12-26 15:17:49
编辑:我只是注意到您想包装一个现有的库而不修改它。
恐怕你那时候运气不好。通常,在没有C编译器阻塞的情况下,从C++代码中提取枚举成员是不可能的。
在实践中,您可以选择是以编程方式将自己的枚举集转换为接口中的C++版本,尝试准确地镜像C++,并放置一堆静态断言进行二次检查,或者在理论上甚至通过脚本过滤掉它们。
恐怕这里没有很好的选择。为了记录在案,我倾向于选择这些糟糕的第一个选项。
就我个人而言,我可能会很懒,只会坚持使用C版本。
尽管如此,如果需要并且常量的数量很大,您可以做一些宏魔术,以便根据需要使用C风格的“名称空间”获得单个定义。
首先,通过宏定义所有枚举条目的单个标头:
/* Fruit.h */
FOO_ENUM(APPLE) = 0,
FOO_ENUM(ORANGE)
然后在C头中:
/* C interface */
typedef enum {
# define FOO_ENUM(id) FOO_##id
# include "Fruit.h"
# undef FOO_ENUM
} Foo_Fruit_t;
最后,在C++头中:
// C++ interface
namespace Foo {
enum Fruit_t {
# define FOO_ENUM(id) id
# include "Fruit.h"
# undef FOO_ENUM
};
}
当然,还有很多其他的选择。例如,如果您不介意污染C++中的全局命名空间,那么始终可以在C接口中直接定义完整的枚举,并在定义的C++版本中复制单个枚举成员。
发布于 2016-08-17 09:56:05
最近,我在一个enums
库的C包装器中遇到了这个特殊的问题,它引起了很大的麻烦。
我的解决方案显示在下面的基本工作示例中,但它在一些地方非常不雅观。它本质上是一种翻译方法。
人们必须警惕,不要在enums
上两度声明任何事情。该示例传递int
、string
或char
数组和enum
。
用C++编写的库头。这是将要包装的库。MyClass.h:
#ifndef __MYCLASS_H
#define __MYCLASS_H
#include <iostream>
namespace MyNamespace {
using namespace std;
enum EnumControlInterface {HIDController=1, UVCController=2};
class MyClass {
private:
int m_i;
string m_text;
EnumControlInterface _controller;
public:
MyClass(int val);
~MyClass();
void int_set(int i);
void string_set(string text);
int int_get();
string string_get();
void writeEnum(EnumControlInterface MyInterface);
EnumControlInterface readEnum();
};
};
#endif
C++实现MyClass.cpp:
#include "MyClass.h"
namespace MyNamespace {
MyClass::MyClass(int val) {
cout << "MyClass is being created" << endl;
cout << "The parameter passed to the MyClass constructor is: " << val << endl;
}
MyClass::~MyClass() {
cout << "MyClass is being destroyed" << endl;
}
void MyClass::writeEnum(EnumControlInterface MyInterface) {
_controller = MyInterface;
cout << "The interface control Enum is set in MyClass.cpp as: " << _controller << endl;
}
EnumControlInterface MyClass::readEnum() {
return _controller;
}
void MyClass::string_set(std::string text) {
m_text = text;
}
string MyClass::string_get() {
return m_text;
}
void MyClass::int_set(int i) {
m_i = i;
}
int MyClass::int_get() {
return m_i;
}
}
一个"C包装器“头文件MyWrapper.h,它包装MyClass.h:
#ifndef __MYWRAPPER_H
#define __MYWRAPPER_H
#ifdef __cplusplus
namespace MyNamespace {
extern "C" {
#endif
typedef enum WrapperEnumControlInterface {WrapHIDController=1, WrapUVCController=2} WrapperEnumControlInterface;
typedef struct MyClass MyClass;
MyClass* newMyClass(int val);
void MyClass_int_set(MyClass* v, int i);
int MyClass_int_get(MyClass* v);
void MyClass_string_set(MyClass* v, char* text);
char* MyClass_string_get(MyClass* v);
void MyClass_writeEnum(MyClass* v, WrapperEnumControlInterface MyInterface);
WrapperEnumControlInterface MyClass_readEnum(MyClass* v);
void deleteMyClass(MyClass* v);
#ifdef __cplusplus
}
}
#endif
#endif
"C包装器“实现是用C和C++混合编写的。具体来说,函数定义必须是C,传递和返回的参数也必须是C类型。在函数内部和预处理器区域内,__cplusplus
、C或C++都应该很好。
例如,不能要求extern "C"
块中的函数接受std::string
类型。它将破坏包装器的目标:只公开操作底层C++库的C代码。extern "C"
确定没有名称损坏的公开内容(请参阅questions about name mangling in C++)。__cplusplus
由(许多) C++编译器定义。
MyWrapper.cc:
#include "MyClass.h"
#include "MyWrapper.h"
#include <vector>
namespace MyNamespace {
extern "C" {
MyClass* newMyClass(int val) {
return new MyClass(val);
}
void deleteMyClass(MyClass* v) {
delete v;
}
void MyClass_int_set(MyClass* v, int i) {
v->int_set(i);
}
int MyClass_int_get(MyClass* v) {
return v->int_get();
}
void MyClass_string_set(MyClass* v, char* text) {
//convert incomming C char* to a C++ string
string stringToSend = string(text);
cout << "the string received from the program by the wrapper is " << text << endl;
cout << "the string sent to the library by the wrapper is " << stringToSend << endl;
v->string_set(stringToSend);
}
char* MyClass_string_get(MyClass* v) {
string result = v->string_get();
cout << "the string received from the library by the wrapper is " << result << endl;
// Convert the C++ string result to a C char pointer and return it. Use vectors to do the memory management.
// A vector type of as many chars as necessary to hold the result string
static vector<char> resultVector(result.begin(), result.end());
cout << "the data in the vector who's pointer is returned to the program by the wrapper is: " << &resultVector[0] << endl;
return (&resultVector[0]);
}
void MyClass_writeEnum(MyClass* v, WrapperEnumControlInterface MyInterface) {
v->writeEnum((EnumControlInterface)MyInterface);
}
WrapperEnumControlInterface MyClass_readEnum(MyClass* v) {
EnumControlInterface result = v->readEnum();
return (WrapperEnumControlInterface)result;
}
}
}
通过包装器Cproject.c调用C++库的C程序:
#include "MyWrapper.h"
#include "stdio.h"
int main(int argc, char* argv[]) {
struct MyClass* clsptr = newMyClass(5);
MyClass_int_set(clsptr, 3);
printf("The int read back in Cproject.c is: %i\n", MyClass_int_get(clsptr));
MyClass_writeEnum(clsptr, WrapUVCController);
printf("The enum read back in Cproject.c is: %d\n", MyClass_readEnum(clsptr));
MyClass_string_set(clsptr, "Hello");
char *textReadBack = MyClass_string_get(clsptr);
printf("The text read back in Cproject.c is: %s \n", textReadBack);
deleteMyClass(clsptr);
return 0;
}
为了完整起见,一个直接调用C++库而不使用包装器CPPProgram.cpp的C++项目就这么短了!
#include "MyClass.h"
#include <iostream>
using namespace std;
using namespace MyNamespace;
int main(int argc, char* argv[]) {
MyClass *c = new MyClass(42);
c->int_set(3);
cout << c->int_get() << endl;
c->writeEnum(HIDController);
cout << c->readEnum() << endl;
c->string_set("Hello");
cout << c->string_get() << endl;
delete c;
}
MyClass C++类被编译到静态库中,包装器被编译到共享库中--没有特殊的原因,两者都可以是静态的或共享的。
调用包装库(Cproject.c)的C程序必须与C++编译器(G++等)链接。
显然,这个例子没有一个真正的应用程序。它在结构上基于c/,但添加了enum
位。
编写包装器的人通常无法访问他们试图包装的库的源代码(在本例中是MyClass.cpp),他们将分别拥有.so或.dll、.a或.lib (用于Linux和.lib)、共享库和静态库。没有必要为C++库提供源代码。只有C++库的头文件才能编写有效的包装器。
我把这个问题写出来,部分是为了为原来的问题提供一个较详尽的答案,这个答案可以很容易地抄写出来,而且可以随意使用,但也因为这是我迄今唯一能够解决问题的方法,而且我认为这是不令人满意的。我希望能够用包装公共成员函数的方式包装enums
,而不是用稍微不同的名称在包装器中重新创建enums
。
经证明有用的相关信息来源:
How to cast / assign one enum value to another enum
Developing C wrapper API for Object-Oriented C++ code
Converting a C-style string to a C++ std::string
Returning pointer from a function
当然,所有不安全、错误等编码实践都是我的错。
https://stackoverflow.com/questions/20792121
复制相似问题