我正在开发一个允许其用户(位于同一进程中的其他库)交换数据缓冲区和流的库。该库必须在MSVC和mingw代码中都可以使用(更多的兼容性不会受到损害,但并非绝对必要)。为了实现这一点,应该从一个小的,与编译器兼容的接口(interface)提供核心功能,该接口(interface)以后可以被使用客户端代码编译的便捷层隐藏。

库的一个具有挑战性的方面是它必须是可扩展的,以便客户端可以提供自己的缓冲区和流实现,但是核心库接口(interface)一旦发布,必须保持稳定。如果您对进一步的背景感兴趣,可以在forum thread discussion中进行阅读。

我试图了解编译器之间的二进制兼容性问题,但是由于我是本主题的新手,所以我会对我的结果发表评论感兴趣。我对这里的标准定义的行为(结构可能无法通过测试)不感兴趣,仅对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);
}

编辑:该项目原本打算搁置一段时间,如果再次搁置,可能需要重新考虑。不过,我还是不提问题,因为我仍然对答案感兴趣。

最佳答案

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

您还将注意到,在Windows header 中,COM接口(interface)的C声明使用struct的方式与您使用的方式几乎相同。

旁注:SharedStreamInterface::destroy成员提出了一个问题,即是否还有一个“创建”这样的流。您可能也想分享。但是你的里程可能会有所不同...

至于调用约定的问题,__cdecl__stdcall都应该跨二进制工作,但是出于其他原因,我总是更喜欢__stdcall:它比__cdecl与更多的“语言”(即工具)兼容。

对于样式:使用#define来显式声明调用约定(如您所做的那样),类似于Windows header 中的WINAPI

07-24 09:45
查看更多