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


Я ищу, чтобы разработать набор API C, которые будут обернуты вокруг наших существующих API C++ для доступа к нашей основной логике (написанной на объектно-ориентированном C++). По сути, это будет API клея, который позволяет нашей логике C++ использоваться другими языками. Каковы некоторые хорошие учебники, книги или рекомендации, которые вводят понятия, связанные с обертыванием C вокруг объектно-ориентированного C++?

6 66

6 ответов:

Это не слишком трудно сделать вручную, но будет зависеть от размера вашего интерфейса. Случаи, когда я это сделал, заключались в том, чтобы использовать нашу библиотеку C++ из чистого кода C, и поэтому SWIG не очень помог. (Ну, может быть, SWIG можно использовать для этого, но я не гуру SWIG, и это казалось нетривиальным)

все, что мы в конечном итоге сделали, было:

  1. каждый объект передается в C непрозрачной ручкой.
  2. конструкторы и деструкторы завернуты в чистую функции
  3. функции-члены являются чистыми функциями.
  4. другие встроенные элементы сопоставляются с эквивалентами C, где это возможно.

Итак, такой класс (заголовок C++)

class MyClass
{
  public:
  explicit MyClass( std::string & s );
  ~MyClass();
  int doSomething( int j );
}

будет отображаться на интерфейс C, как это (заголовок C):

struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );

реализация интерфейса будет выглядеть так (Источник C++)

#include "MyClass.h"

extern "C" 
{
  HMyClass * myStruct_create( const char * s )
  {
    return reinterpret_cast<HMyClass*>( new MyClass( s ) );
  }
  void myStruct_destroy( HMyClass * v )
  {
    delete reinterpret_cast<MyClass*>(v);
  }
  int myStruct_doSomething( HMyClass * v, int i )
  {
    return reinterpret_cast<MyClass*>(v)->doSomething(i);
  }
}

мы выводим нашу непрозрачную ручку из исходного класса, чтобы избежать необходимости какого-либо литья, и (это не похоже на работу с моим нынешним компилятора). Мы должны сделать дескриптор структурой, поскольку C не поддерживает классы.

Итак, что дает нам основной интерфейс c. Если вы хотите более полный пример, показывающий один из способов интеграции обработки исключений, то вы можете попробовать мой код на github:https://gist.github.com/mikeando/5394166

забавная часть теперь гарантирует, что вы получите все необходимые библиотеки C++, связанные с вами большую библиотеку правильно. Для gcc (или clang) это означает просто выполнение заключительного этапа ссылки с использованием g++.

Я думаю, что ответ Майкла Андерсона находится на правильном пути, но мой подход был бы другой. Вы должны беспокоиться об одной дополнительной вещи: исключения. Исключения не являются частью C ABI, поэтому вы не можете позволить исключениям когда-либо выбрасываться за код C++. Так что ваш заголовок будет выглядеть так:

#ifdef __cplusplus
extern "C"
{
#endif
    void * myStruct_create( const char * s );
    void myStruct_destroy( void * v );
    int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif

и твоя обертка .cpp файл будет выглядеть так:

void * myStruct_create( const char * s ) {
    MyStruct * ms = NULL;
    try { /* The constructor for std::string may throw */
        ms = new MyStruct(s);
    } catch (...) {}
    return static_cast<void*>( ms );
}

void myStruct_destroy( void * v ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    delete ms;
}

int myStruct_doSomething( void * v, int i ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    int ret_value = -1; /* Assuming that a negative value means error */
    try {
        ret_value = ms->doSomething(i);
    } catch (...) {}
    return ret_value;
}

еще лучше: если вы знаете, что все, что вам нужно, как один экземпляр MyStruct, не рискуйте работы с указателями void, передаваемыми в ваш API. Сделать что-то вроде этого:

static MyStruct * _ms = NULL;

int myStruct_create( const char * s ) {
    int ret_value = -1; /* error */
    try { /* The constructor for std::string may throw */
        _ms = new MyStruct(s);
        ret_value = 0; /* success */
    } catch (...) {}
    return ret_value;
}

void myStruct_destroy() {
    if (_ms != NULL) {
        delete _ms;
    }
}

int myStruct_doSomething( int i ) {
    int ret_value = -1; /* Assuming that a negative value means error */
    if (_ms != NULL) {
        try {
            ret_value = _ms->doSomething(i);
        } catch (...) {}
    }
    return ret_value;
}

этот API намного безопаснее.

но, как упоминал Майкл, связывание может стать довольно сложным.

надеюсь, что это помогает

нетрудно представить код C++ на C, просто используйте шаблон дизайна фасада

Я предполагаю, что ваш код C++ встроен в библиотеку, все, что вам нужно сделать, это сделать один модуль C в вашей библиотеке C++ в качестве фасада вашей библиотеки вместе с чистым файлом заголовка C. Модуль C вызовет соответствующие функции C++

Как только вы это сделаете, ваши приложения и библиотека C будут иметь полный доступ к api C, который вы выставили.

например, вот пример фасада модуль

#include <libInterface.h>
#include <objectedOrientedCppStuff.h>

int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
      Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
      obj->doStuff(arg2);
      return obj->doMoreStuff(arg1);
   }

затем вы выставляете эту функцию C в качестве своего API, и вы можете использовать ее свободно как C lib, не беспокоясь о

// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);

очевидно, что это надуманный пример, но это самый простой способ выставить библиотеку C++ на C

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

SWIG-это инструмент разработки программного обеспечения, который соединяет программы, написанные на C и C++ с различными языками программирования высокого уровня. SWIG используется с различные типы языков, включая общие языки сценариев, такие как Perl, PHP, Python, Tcl и Ruby. Список поддерживаемых языков включает не-скриптовых языков, таких как C#, Общие на Lisp (clisp может, Аллегро КЛ, на корейском языке, UFFI), Ява, Луа, Модула-3, данные, используемые, октавы и Р. Также несколько интерпретируется и компилируется схема реализации (лукавства, MzScheme, курица) поддерживаются. SWIG чаще всего используется для создания высокоуровневых интерпретируемых или компилируемых сред программирования, пользовательских интерфейсов и как инструмент для тестирования и прототипирования программного обеспечения C / C++. SWIG также может экспортировать свое дерево синтаксического анализа в виде XML и Lisp s-выражений. Глоток может свободно использоваться, распространяться и модифицироваться для коммерческого и некоммерческого использования.

просто замените понятие объекта на void * (часто упоминается как непрозрачный тип в библиотеках, ориентированных на C) и повторно использовать все, что вы знаете из C++.

Я думаю, что использование SWIG является лучшим ответом... он не только избегает изобретать колесо, но и надежен, а также способствует непрерывности развития, а не одной съемке проблемы.

высокочастотные проблемы должны быть решены с помощью долгосрочного решения.