Разработка API оболочки C для объектно-ориентированного кода C++
Я ищу, чтобы разработать набор API C, которые будут обернуты вокруг наших существующих API C++ для доступа к нашей основной логике (написанной на объектно-ориентированном C++). По сути, это будет API клея, который позволяет нашей логике C++ использоваться другими языками. Каковы некоторые хорошие учебники, книги или рекомендации, которые вводят понятия, связанные с обертыванием C вокруг объектно-ориентированного C++?
6 ответов:
Это не слишком трудно сделать вручную, но будет зависеть от размера вашего интерфейса. Случаи, когда я это сделал, заключались в том, чтобы использовать нашу библиотеку C++ из чистого кода C, и поэтому SWIG не очень помог. (Ну, может быть, SWIG можно использовать для этого, но я не гуру SWIG, и это казалось нетривиальным)
все, что мы в конечном итоге сделали, было:
- каждый объект передается в C непрозрачной ручкой.
- конструкторы и деструкторы завернуты в чистую функции
- функции-члены являются чистыми функциями.
- другие встроенные элементы сопоставляются с эквивалентами 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-выражений. Глоток может свободно использоваться, распространяться и модифицироваться для коммерческого и некоммерческого использования.