这个接口在MSVC和mingw之间二进制兼容吗?

问题描述 投票:0回答:1

我正在开发一个库,允许其用户(驻留在同一进程中的其他库)交换数据缓冲区和流。该库必须可以从 MSVC 和 mingw 代码中使用(更高的兼容性不会有什么坏处,但并不是绝对必要的)。为了实现这一目标,应通过一个小型的、与编译器兼容的接口提供核心功能,该接口稍后可以通过与客户端代码一起编译的便利层隐藏。

该库的一个具有挑战性的方面是它必须是可扩展的,以便客户端可以提供自己的缓冲区和流实现,但核心库接口一旦发布就必须保持稳定。如果您对更多背景感兴趣,可以在论坛主题讨论中阅读。

我曾尝试了解编译器之间的二进制兼容性问题,但由于我是这个主题的新手,我会对对我的结果的评论感兴趣。我对这里标准定义的行为不感兴趣(结构可能无法通过测试),只对 mingw 和 MSVC 以及 maybe 其他编译器(如果方便的话)之间的兼容性感兴趣。

特别是,结构是否兼容?它们统一由函数指针组成,因此我认为填充不会成为问题。此外,这里是否需要 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);
}

编辑:这个项目已经被搁置了一段时间,如果它再次被搁置,可能需要重新思考。不过我还是把问题留着,因为我仍然对答案感兴趣。

visual-c++ mingw calling-convention binary-compatibility
1个回答
3
投票

是的,它们会兼容。这就是

struct
的美妙之处。只要您不引入填充问题(正如您正确指出的那样,这里确实不会出现这种情况),或者在 C++ 中向
struct
添加功能,这将导致 - 编译器特定的 - vtable 布局,这将始终兼容。

您还会注意到,从 Windows 标头中,COM 接口的 C 声明使用

struct
的方式与您几乎相同。

旁注:

SharedStreamInterface::destroy
成员提出了一个问题:是否也有一个人可以“创建”这样一个流。您可能也想分享这一点。但您的里程可能会有所不同...... 至于调用约定的问题,

__cdecl

__stdcall
应该可以在二进制文件中工作,但是我总是更喜欢__stdcall
,因为另一个原因:它与更多“语言”(即工具)兼容比
__cdecl

对于样式:使用

#define

 显式声明调用约定(就像您所做的那样),类似于 Windows 标头中的 
WINAPI

© www.soinside.com 2019 - 2024. All rights reserved.