Этот двоичный интерфейс совместим между MSVC и mingw?
Я работаю над библиотекой, которая позволяет ее пользователям (другим библиотекам, находящимся в том же процессе) обмениваться буферами данных и потоками. Библиотеку нужно использовать как из кода MSVC, так и из кода mingw (больше совместимости не повредит, но не является строго обязательным). Для достижения этой цели базовая функциональность должна обеспечиваться из небольшого, совместимого с компилятором интерфейса, который впоследствии может быть скрыт с помощью удобного уровня, который компилируется с клиентским кодом.
Сложным аспектом библиотеки является то, что она должна быть расширяемой, чтобы клиенты могли предоставлять свои собственные реализации буфера и потока, но интерфейс базовой библиотеки должен оставаться стабильным после ее освобождения. Если вы заинтересованы в дополнительной информации, вы можете прочитать об этом в обсуждении на форуме.
Я пытался узнать о проблемах бинарной совместимости между компиляторами, но, поскольку я новичок в этой теме, мне будут интересны комментарии к моему результату. Меня не интересует поведение, определяемое стандартами (структуры, вероятно, не проходят этот тест), только совместимость между mingw и MSVC и, возможно, другими компиляторами, если это возможно.
В частности, будут ли структуры совместимы? Они равномерно состоят из указателей функций, поэтому я не думаю, что заполнение будет проблемой. Кроме того, здесь ли необходимо соглашение stdcall, или cdecl будет работать так же хорошо? Могу ли я оставить это неопределенным, так как оба компилятора по умолчанию будут cdecl? Нужно ли мне? Вот что у меня сейчас есть:
#include <stdint.h>
typedef struct {
uint32_t (__stdcall *read)(void*, uint8_t*, uint32_t);
void (__stdcall *write)(void*, const uint8_t*, uint32_t);
uint32_t (__stdcall *getBytesLeft)(void*);
uint8_t (__stdcall *destroy)(void*);
} SharedStreamInterface;
typedef struct {
uint32_t (__stdcall *read)(void*, uint8_t*, uint32_t);
void (__stdcall *write)(void*, const uint8_t*, uint32_t);
uint32_t (__stdcall *getBytesLeft)(void*);
uint8_t (__stdcall *destroy)(void*);
uint32_t (__stdcall *getreadpos)(void*);
uint32_t (__stdcall *getwritepos)(void*);
uint32_t (__stdcall *getlength)(void*);
void (__stdcall *setreadpos)(void*, uint32_t);
void (__stdcall *setwritepos)(void*, uint32_t);
void (__stdcall *setlength)(void*, uint32_t);
} SharedBufferInterface;
extern "C" {
// Functions applicable for both buffers and streams
__stdcall uint32_t readData(uint32_t id, uint8_t* data, uint32_t size);
__stdcall void writeData(uint32_t id, const uint8_t* data, uint32_t size);
__stdcall uint32_t getBytesLeft(uint32_t id);
__stdcall void destroyStreamOrBuffer(uint32_t id);
__stdcall uint8_t streamOrBufferExists(uint32_t id);
// Functions only applicable for buffers
__stdcall uint32_t getReadPos(uint32_t id);
__stdcall uint32_t getWritePos(uint32_t id);
__stdcall uint32_t getLength(uint32_t id);
__stdcall void setReadPos(uint32_t id, uint32_t pos);
__stdcall void setWritePos(uint32_t id, uint32_t pos);
__stdcall void setLength(uint32_t id, uint32_t length);
__stdcall uint8_t bufferExists(uint32_t id);
// Adding new buffers/Streams
__stdcall uint32_t addStream(SharedStreamInterface *interface, void *stream);
__stdcall uint32_t addBuffer(SharedBufferInterface *interface, void *buffer);
}
Редактировать: проект, для которого это было предназначено, был приостановлен в течение некоторого времени и, вероятно, нуждается в большом переосмыслении, если он когда-либо снова останется без внимания. Я оставляю вопрос, хотя, потому что я все еще заинтересован в ответе.
1 ответ
Да, они будут совместимы. Это красота с struct
s. До тех пор, пока вы не столкнетесь с проблемами заполнения (что на самом деле здесь не так, как вы правильно указали) или в C++ добавьте функциональность к struct
Если это приведет к компиляции - vtable - это будет всегда совместимо.
Вы также заметите, что из заголовков Windows используются объявления C интерфейсов COM. struct
во многом так же, как вы делаете.
Примечание: SharedStreamInterface::destroy
member напрашивается вопрос, есть ли еще один, чтобы "создать" такой поток. Вы можете также поделиться этим. Но ваш пробег может варьироваться...
Что касается вопроса о соглашении о вызовах, то оба __cdecl
а также __stdcall
должен работать через двоичные файлы, однако я бы всегда предпочел __stdcall
по другой причине: он совместим с большим количеством "языков" (т. е. инструментов), чем __cdecl
,
Для стиля: используйте #define
объявить соглашение о вызовах явно (как и вы), аналогично WINAPI
из заголовков Windows.