Ваш компилятор может абсолютно поднять переменные-члены Point в регистры, если это необходимо. Это, однако, не то же самое, что компилятор, преобразующий сам вызов функции в передачу по значению.
Вы должны проверить сгенерированную сборку, чтобы увидеть, какие оптимизации выполняются.
И FWIW, общее правило, которое я использую, состоит в том, чтобы передавать все примитивные типы по значению и все классы / UDT (POD или нет) по константной ссылке, когда это возможно, и позволить компилятору разобраться с лучшим вариантом. Мы не должны беспокоиться о деталях того, что делает компилятор, он намного умнее нас.
Каждому общедоступному методу необходима функция 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);
}
Во-первых, вам может не понадобиться преобразовывать все ваши методы в функции C. Если вы можете упростить API и скрыть часть интерфейса C ++, это будет лучше, поскольку вы минимизируете возможность изменения C API при изменении логики C ++.
Так что подумайте о более высоком уровне абстракции, который должен быть предоставлен через этот API. Используйте описанное вами решение void *.
Некоторые мнения из моего опыта:
Например:
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;
}
Например:
C_ERROR CreateUi(HUI* ui);
C_ERROR CloseUi(HUI hui); // usually error codes don't matter here, so may use void
Например:
C_ERROR CreateBitmap(HUI* ui, SIZE size, char** pBmpBuffer, int* pSize);
Image
и вы предоставляете доступ к нему через дескриптор HIMG
, предоставьте функции для его преобразования, например, в окна HBITMAP и обратно). Это упростит интеграцию с существующим API. Например,
C_ERROR BitmapToHBITMAP(HUI* ui, char* bmpBuffer, int size, HBITMAP* phBmp);
Используйте вектор (и строку :: c_str) для обмена данными с API, отличными от C ++. (Директива № 78 из Стандарты кодирования C ++ , Х. Саттер / А. Александреску).
PS Это неправда, что «конструкторы могут сохранять свой исходный список аргументов». Это верно только для типов аргументов, которые совместимы с C.
PS2 Конечно, слушайте Cătălin и старайтесь сделать свой интерфейс как можно более маленьким и простым.
Это может быть интересно: «Смешивание C и C ++» в C ++ FAQ Lite. В частности [32.8] Как я могу передать объект класса C ++ в / из функции C?