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

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

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