首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将C++命名空间枚举封装到C接口

将C++命名空间枚举封装到C接口
EN

Stack Overflow用户
提问于 2013-12-26 22:32:05
回答 2查看 2.4K关注 0票数 1

我正在尝试将现有的第三方 C++库封装到C接口,以便它可以用于另一种语言的绑定。我很难弄清楚如何包装一个名称空间枚举,而不是仅仅重新定义它:

代码语言:javascript
运行
复制
// Existing C++ 3rd party library header
namespace foo {
    enum Fruit {
        APPLE = 0,
        ORANGE
    }
}

所以我用一个extern "C"块包装了我的.{h,cpp},我只是不知道如何将foo::Fruit枚举导出到C接口中。

代码语言:javascript
运行
复制
// 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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-26 23:17:49

编辑:我只是注意到您想包装一个现有的库而不修改它。

恐怕你那时候运气不好。通常,在没有C编译器阻塞的情况下,从C++代码中提取枚举成员是不可能的。

在实践中,您可以选择是以编程方式将自己的枚举集转换为接口中的C++版本,尝试准确地镜像C++,并放置一堆静态断言进行二次检查,或者在理论上甚至通过脚本过滤掉它们。

恐怕这里没有很好的选择。为了记录在案,我倾向于选择这些糟糕的第一个选项。

就我个人而言,我可能会很懒,只会坚持使用C版本。

尽管如此,如果需要并且常量的数量很大,您可以做一些宏魔术,以便根据需要使用C风格的“名称空间”获得单个定义。

首先,通过宏定义所有枚举条目的单个标头:

代码语言:javascript
运行
复制
/* Fruit.h */
FOO_ENUM(APPLE) = 0,
FOO_ENUM(ORANGE)

然后在C头中:

代码语言:javascript
运行
复制
/* C interface */
typedef enum {
#   define FOO_ENUM(id) FOO_##id
#   include "Fruit.h"
#   undef FOO_ENUM
} Foo_Fruit_t;

最后,在C++头中:

代码语言:javascript
运行
复制
// C++ interface
namespace Foo {
    enum Fruit_t {
#       define FOO_ENUM(id) id
#       include "Fruit.h"
#       undef FOO_ENUM
    };
}

当然,还有很多其他的选择。例如,如果您不介意污染C++中的全局命名空间,那么始终可以在C接口中直接定义完整的枚举,并在定义的C++版本中复制单个枚举成员。

票数 3
EN

Stack Overflow用户

发布于 2016-08-17 17:56:05

最近,我在一个enums库的C包装器中遇到了这个特殊的问题,它引起了很大的麻烦。

我的解决方案显示在下面的基本工作示例中,但它在一些地方非常不雅观。它本质上是一种翻译方法。

人们必须警惕,不要在enums上两度声明任何事情。该示例传递intstringchar数组和enum

用C++编写的库头。这是将要包装的库。MyClass.h:

代码语言:javascript
运行
复制
#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:

代码语言:javascript
运行
复制
#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:

代码语言:javascript
运行
复制
#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:

代码语言:javascript
运行
复制
#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程序:

代码语言:javascript
运行
复制
#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++项目就这么短了!

代码语言:javascript
运行
复制
#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

经证明有用的相关信息来源:

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

*

当然,所有不安全、错误等编码实践都是我的错。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20792121

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档