Обернуть перечисление пространства имен C ++ в интерфейс C

0

Я пытаюсь обернуть существующую стороннюю библиотеку C++ в интерфейс C, чтобы ее можно было использовать в привязках для другого языка. У меня возникли проблемы с выяснением того, как обернуть переименованное пространство имен, а не просто переопределить его:

// Existing C++ 3rd party library header
namespace foo {
    enum Fruit {
        APPLE = 0,
        ORANGE
    }
}

Итак, у меня есть мой завернутый. {H, cpp} с extern "C" блоком 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?

  • 1
    Конечно, есть способы сделать это, самый быстрый и самый грязный, дословно являющийся #include в обоих местах, но, вероятно, вы также хотели бы добавить префикс C-версии к какому-то ручному «пространству имен»? Я предполагаю, что вы, вероятно, в конечном итоге либо вручную скопируете присваивания значений enum в версии C ++, либо будете использовать макросы для манипулирования идентификаторами.
  • 0
    @doynax Да, до сих пор мне приходилось просто вручную копировать определение перечисления прямо в мой extern "C" потому что я не могу ссылаться на исходное перечисление пространства имен в сторонней библиотеке. Есть ли решение для макроса?
Показать ещё 1 комментарий
Теги:
enums
extern

2 ответа

2
Лучший ответ

edit: Я просто заметил, что вы хотите обернуть существующую библиотеку, не изменяя ее.

Тогда я боюсь, что тебе повезло. В общем, просто невозможно извлечь только членов перечисления из кода 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++ версию определения.

  • 0
    Fruit.inc было бы использовать Fruit.inc : это не заголовочный файл, но он должен быть #include 'd. В противном случае, это совершенно нормальное (в конце концов, это взаимодействие C / C ++).
  • 0
    Я не так уж и против того, чтобы просто копировать определения перечислений, поскольку в конечном итоге это выглядит примерно так же, как и при использовании макросов, поскольку вам все равно придется выписывать их все. Я надеялся на какое-то волшебное решение typedef которое псевдоним foo::Fruit to Fruit . Ну что ж. В случае, если это вообще имеет значение для вашего ответа, в частности, я обертываю эту библиотеку , чтобы я мог создавать привязки для языка Go .
Показать ещё 3 комментария
1

Я столкнулся с этой конкретной проблемой с enums в C-обертке для библиотеки 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 wrapper" 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 wrapper" написана в виде смеси C и C++. В частности, определения функций должны быть C, а переданные и возвращаемые параметры также должны быть типами C. Внутри функций и внутри областей препроцессора __cplusplus C или C++ должно быть хорошо.

Например, нельзя запросить функцию внутри блока extern "C" чтобы принять тип std::string. Это приведет к поражению цели оболочки: выставить только код C, который управляет базовой библиотекой C++. extern "C" определяет, что отображается без искажения имени (см. вопросы об изменении имени в 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 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;
    }

}
}

AC, которая вызывает библиотеку C++ через обертку Cproject.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++ скомпилирован в статическую библиотеку, оболочка скомпилирована в общую библиотеку, нет особой причины, оба могут быть статическими или совместно используемыми.

Программа C, вызывающая библиотеку обертки (Cproject.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/

Как передать/присвоить одно значение enum другому перечислению

Разработка API-интерфейса оболочки для объектно-ориентированного кода C++

Преобразование строки в стиле C в строку C++ std ::

Возвращающий указатель от функции

std :: string to char *

Конечно, все небезопасные, неправильные и т.д. Методы кодирования - это моя ошибка полностью.

  • 0
    Таким образом, ключевым моментом в этом ответе является то, что вы действительно копируете определение enum в свою оболочку C и просто приводите между завернутыми и исходными значениями. Это то, что я говорил, я не хотел делать. Причина в том, что я хотел избежать синхронизации упакованного перечисления с любыми изменениями в исходном перечислении.
  • 0
    В самом деле, как я сказал в ответе: «Это единственный способ, которым я смог решить проблему до сих пор, и, на мой взгляд, он не является удовлетворительным». Я надеюсь, что если я сделаю так, что кому-то, кому весь день платят за программирование, будет очень легко скомпилировать пример, он пожалеет меня и предоставит тот же пример, но сделает другой способ, которым обернуты enums , что некоторые из них означают, что Я не знаю.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню