Я хочу разработать набор API-интерфейсов C, которые будут охватывать наши существующие API С++ для доступа к нашей основной логике (написанной в объектно-ориентированном С++). Это будет, по сути, API-интерфейсом клея, который позволяет использовать нашу С++-логику на других языках. Какие хорошие учебники, книги или лучшие практики, которые вводят понятия, связанные с обертыванием C вокруг объектно-ориентированного С++?
Разработка API-интерфейсов C для объектно-ориентированного кода на С++
Ответ 1
Это не слишком сложно сделать вручную, но будет зависеть от размера вашего интерфейса. Случаи, когда я это делал, заключались в том, чтобы разрешить использование нашей библиотеки С++ из чистого кода C, и поэтому SWIG не помог. (Ну, возможно, SWIG можно использовать для этого, но я не гуру SWIG, и это казалось нетривиальным)
В итоге мы закончили:
- Каждый объект передается в 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 );
Реализация интерфейса будет выглядеть так (источник С++)
#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 p >
Интересная часть теперь гарантирует, что вы получите все необходимые библиотеки С++, связанные с вами в более крупной библиотеке. Для gcc (или clang), который означает только выполнение заключительной стадии ссылки с использованием g++.
Ответ 2
Я думаю, что ответ Майкла Андерсона на правильном пути, но мой подход будет другим. Вы должны беспокоиться об одной дополнительной вещи: Исключения. Исключения не являются частью C ABI, поэтому вы не можете позволить исключаться из-за кода С++. Итак, ваш заголовок будет выглядеть следующим образом:
#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 намного безопаснее.
Но, как сказал Майкл, связь может стать довольно сложной.
Надеюсь, что это поможет
Ответ 3
Нетрудно выставить код С++ на C, просто используйте шаблон дизайна Facade
Я предполагаю, что ваш код на С++ встроен в библиотеку, все, что вам нужно сделать, это сделать один модуль C в вашей библиотеке С++ как фасад в вашей библиотеке вместе с чистым файлом заголовка C. Модуль C вызовет соответствующие функции С++
Как только вы это сделаете, ваши приложения и библиотека C будут иметь полный доступ к C api, который вы выставили.
например, здесь приведен пример модуля Facade
#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
Ответ 4
Я бы подумал, что вы сможете получить некоторые идеи по направлению и/или, возможно, напрямую использовать SWIG. Я бы подумал, что рассмотрение нескольких примеров, по крайней мере, даст вам представление о том, какие вещи следует учитывать при переносе одного API в другой. Упражнение может быть полезным.
SWIG - это инструмент для разработки программного обеспечения, который соединяет программы, написанные на C и С++, с различными языками программирования высокого уровня. SWIG используется с различными типами языков, включая обычные языки сценариев, такие как Perl, PHP, Python, Tcl и Ruby. Список поддерживаемых языков также включает языки без скриптов, такие как С#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave и R. Также несколько интерпретированных и скомпилированных схем (Guile, MzScheme, Chicken). SWIG чаще всего используется для создания высокоуровневых интерпретируемых или скомпилированных сред программирования, пользовательских интерфейсов и в качестве инструмента для тестирования и прототипирования программного обеспечения C/С++. SWIG также может экспортировать свое дерево разбора в виде XML и Lisp s-выражений. SWIG может свободно использоваться, распространяться и модифицироваться для коммерческого и некоммерческого использования.
Ответ 5
Просто замените концепцию объекта на void *
(часто называемый непрозрачным типом в C-ориентированных библиотеках) и повторно используйте все, что вы знаете, из С++.
Ответ 6
Я думаю, что использование SWIG - лучший ответ... он не только избегает изобретать колесо, но и надежен, а также способствует непрерывности в развитии, а не одной проблеме.
Проблемы с высокой частотой должны решаться с помощью долгосрочного решения.