我正在尝试将现有的第三方 C ++库包装到C接口,以便它可以用于另一种语言的绑定。我无法弄清楚如何包装命名空间枚举,而不是重新定义它:
// Existing C++ 3rd party library header
namespace foo {
enum Fruit {
APPLE = 0,
ORANGE
}
}
那么我的包裹。{h,cpp}包含extern "C"
块,我无法弄清楚如何将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
?
答案 0 :(得分:3)
编辑:我刚注意到你想要包装现有的库而不修改它。
我担心你的运气不好。通常,如果没有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 ++版本中复制各个枚举成员。
答案 1 :(得分:1)
我最近在C ++库的C包装器中遇到了enums
的这个特殊问题,这引起了很大的麻烦。
我的解决方案在以下几乎是最小的工作示例中显示,但它在某些地方非常不优雅。它本质上是一种翻译方法。
必须警惕不要对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
MyClass.cpp的C ++实现:
#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;
}
}
A&#34; C包装&#34;包装MyClass.h的头文件MyWrapper.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
&#34; C包装&#34;实现是用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 ++项目直接调用C ++库而不使用包装器CPPProgram.cpp,所以很简单!:
#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 ++等)链接。
显然,这个例子并没有严肃的应用。它在结构方面基于https://www.teddy.ch/c++_library_in_c/,但添加了enum
位。
编写包装器的人通常无法访问他们试图包装的库的源代码(在这种情况下为MyClass.cpp),他们将拥有.so或.dll或。 a或.lib分别用于Linux和Windows共享和静态库。没有必要拥有C ++库的源代码。只需要C ++库的头文件来编写有效的包装器。
我写这篇文章的部分原因是为原始问题提供了一个更详细的答案,一个可以轻松复制编译并且可以使用的问题,但也因为这是我迄今为止能够解决问题的唯一方法。在我看来,这并不令人满意。我希望能够以包装公共成员函数的方式包装enums
,而不是在包装器中重新创建enums
,名称略有不同。
证明有用的相关信息来源:
https://www.teddy.ch/c++_library_in_c/
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
当然,所有不安全,错误等编码习惯都是我的错。