Обтекание класса C ++ API для потребления C

Ваш компилятор может абсолютно поднять переменные-члены Point в регистры, если это необходимо. Это, однако, не то же самое, что компилятор, преобразующий сам вызов функции в передачу по значению.

Вы должны проверить сгенерированную сборку, чтобы увидеть, какие оптимизации выполняются.

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

33
задан Vertexwahn 24 July 2017 в 21:32
поделиться

5 ответов

Каждому общедоступному методу необходима функция C.
Вам также понадобится непрозрачный указатель для представления вашего класса в коде C.
Проще просто использовать void *, хотя вы можете создать структуру, содержащую void * и другую информацию (например, если вы хотите поддерживать массивы?).

Fred.h
--------------------------------

#ifdef  __cplusplus
class Fred
{
    public:
    Fred(int x,int y);
    int doStuff(int p);
};
#endif

//
// C Interface.
typedef void*   CFred;

//
// Need an explicit constructor and destructor.
extern "C" CFred  newCFred(int x,int y);
extern "C" void   delCFred(CFred);

//
// Each public method. Takes an opaque reference to the object
// that was returned from the above constructor plus the methods parameters.
extern "C" int    doStuffCFred(CFred,int p);

Реализация тривиальна.
Преобразуйте непрозрачный указатель в Fred и затем вызовите метод.

CFred.cpp
--------------------------------

// Functions implemented in a cpp file.
// But note that they were declared above as extern "C" this gives them
// C linkage and thus are available from a C lib.
CFred newCFred(int x,int y)
{
    return reinterpret_cast<void*>(new Fred(x,y));
}

void delCFred(CFred fred)
{
    delete reinterpret_cast<Fred*>(fred);
}

int doStuffCFred(CFred fred,int p)
{
    return reinterpret_cast<Fred*>(fred)->doStuff(p);
}
31
ответ дан 27 November 2019 в 18:14
поделиться

Во-первых, вам может не понадобиться преобразовывать все ваши методы в функции C. Если вы можете упростить API и скрыть часть интерфейса C ++, это будет лучше, поскольку вы минимизируете возможность изменения C API при изменении логики C ++.

Так что подумайте о более высоком уровне абстракции, который должен быть предоставлен через этот API. Используйте описанное вами решение void *.

4
ответ дан 27 November 2019 в 18:14
поделиться

Некоторые мнения из моего опыта:

  • функции должны возвращать коды для представления ошибок. Полезно иметь функцию, возвращающую описание ошибки в строковой форме. Все остальные возвращаемые значения должны быть параметрами out.

Например:

C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget);
  • помещать подписи в структуры / классы, на которые указывает указатель ваших дескрипторов, для проверки дескрипторов на валидность.

Например, ваша функция должна выглядеть так:

C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget){
    Ui* ui = (Ui*)ui;
    if(ui.Signature != 1234)
    return BAD_HUI;
}
  • объекты должны создаваться и освобождаться с использованием функций, экспортированных из DLL, так как метод распределения памяти в DLL и потребляющее приложение могут отличаться.

Например:

C_ERROR CreateUi(HUI* ui);
C_ERROR CloseUi(HUI hui); // usually error codes don't matter here, so may use void
  • если вы выделяете память для некоторого буфера или других данных, которые могут потребоваться для сохранения вне вашей библиотеки, укажите размер этого буфера / данных. Таким образом, пользователи могут сохранить его на диск, в БД или где угодно, не взламывая ваши внутренние компоненты, чтобы узнать фактический размер. В противном случае вы В конечном итоге мне понадобится предоставить свой собственный API ввода-вывода файла, который пользователи будут использовать только для преобразования ваших данных в массив байтов известного размера.

Например:

C_ERROR CreateBitmap(HUI* ui, SIZE size, char** pBmpBuffer, int* pSize);
  • если ваши объекты имеют типичное представление вне вашей библиотеки C ++, предоставьте средство преобразования в это представление (например, если у вас есть класс Image и вы предоставляете доступ к нему через дескриптор HIMG , предоставьте функции для его преобразования, например, в окна HBITMAP и обратно). Это упростит интеграцию с существующим API.

Например,

C_ERROR BitmapToHBITMAP(HUI* ui, char* bmpBuffer, int size, HBITMAP* phBmp);
3
ответ дан 27 November 2019 в 18:14
поделиться

Используйте вектор (и строку :: c_str) для обмена данными с API, отличными от C ++. (Директива № 78 из Стандарты кодирования C ++ , Х. Саттер / А. Александреску).

PS Это неправда, что «конструкторы могут сохранять свой исходный список аргументов». Это верно только для типов аргументов, которые совместимы с C.

PS2 Конечно, слушайте Cătălin и старайтесь сделать свой интерфейс как можно более маленьким и простым.

2
ответ дан 27 November 2019 в 18:14
поделиться
2
ответ дан 27 November 2019 в 18:14
поделиться