库头文件中的结构定义和编译差异

发布于 2024-12-21 15:57:31 字数 317 浏览 0 评论 0原文

我有一个代码被编译成一个库(dll、静态库等)。我希望这个库的用户使用一些结构来传递一些数据作为库函数的参数。我考虑过在 API 头文件中声明该结构体。

  • 考虑到使用不同的编译器进行编译,相对于结构对齐或其他我没有考虑到的事情,这样做是否安全?
  • 库及其用户是否需要使用相同的编译器(和标志)?

几点说明:

  1. 我考虑过给用户一个指针并通过库中的函数设置所有结构,但这将使 API 使用起来并不舒服。
  2. 这个问题是关于C的,尽管很高兴知道c++中是否存在差异。

I have a code which is compiled into a library (dll, static library and so). I want the user of this library to use some struct to pass some data as parameters for the library function. I thought about declaring the struct in the API header file.

  • Is it safe to do so, considering compilation with different compilers, with respect to structure alignment or other things I didn't think about?
  • Will it require the usage of the same compiler (and flags) for both the library and its user?

Few notes:

  1. I considered giving the user a pointer and set all the struct via functions in the library, but this will make the API really not comfortable to use.
  2. This question is about C, although it would be nice to know if there's a difference in c++.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

流绪微梦 2024-12-28 15:57:31

如果它是常规/静态库,则应使用相同的编译器来编译库和应用程序。我能想到的有几个原因:

  1. 不同的编译器(如不同品牌或不同平台的编译器)通常不理解彼此的对象和库格式。
  2. 您不想使用不同类型(例如,signed 与 unsigned char)、类型大小(例如,long = 32 与 64 位)、对齐和打包以及可能的其他一些东西来编译同一程序的不同部分,所有这些都是允许的由C标准来改变。混合和匹配这些东西通常是一件坏事。

但是,您可能经常使用同一编译器的略有不同版本来编译库和使用它的应用程序。通常情况下,没问题。但有时某些更改会破坏代码。

您可以在该头文件中实现一些“初始化”函数(声明为静态内联),以确保类型、类型大小、对齐和打包与编译库的预期相同。使用此库的应用程序必须在使用库的任何其他部分之前调用此函数。如果情况与预期不一样,则该函数一定会失败并导致程序终止,可能还会有一些对失败的良好文字描述。这并不能完全解决编译器有些不兼容的问题,但它可以防止无声的和神秘的故障。有些事情可以使用预处理器的 #if#ifdef 指令进行检查,并导致 #error 编译错误。

此外,可以通过在结构声明中插入显式填充字节并强制紧密打包(例如,通过使用许多编译器都支持的#pragma pack)来缓解结构打包问题。这样,如果字体大小相同,则默认包装是什么并不重要。

您也可以将相同的方法应用于 DLL,但您确实应该期望调用应用程序是使用不同的编译器编译的,而不是依赖于相同的编译器。

If it's a regular/static library, the library and application should be compiled using the same compiler. There're a few reasons for this that I can think of:

  1. Different compilers (as in different brands or compilers for different platforms) normally don't understand each other's object and library formats.
  2. You don't want to compile different parts of the same program using different types (e.g. signed vs unsigned char), type sizes (e.g. long = 32 vs 64 bits), alignment and packing and probably some other things, all of which are allowed by the C standard to vary. Mixing and matching those things is usually a bad thing.

You may, however, often use slightly different versions of the same compiler to compile the library and the application using it. Usually, it's OK. Sometimes there're changes that break the code, though.

You may implement some "initialization" function in that header file (declared as static inline) that would ensure that types, type sizes, alignment and packing are the same as expected by the compiled library. The application using this library would have to call this function prior to using any other part of the library. If things aren't the same as expected, the function must fail and cause program termination, possibly with some good textual description of the failure. This won't solve completely the problem of having somewhat incompatible compilers, but it can prevent silent and mysterious malfunctions. Some things can be checked with the preprocessor's #if and #ifdef directives and cause compilation errors with #error.

In addition, structure packing problems can be relieved by inserting explicit padding bytes into structure declarations and forcing tight packing (by e.g. using #pragma pack, which is supported by many compilers). That way if type sizes are the same, it won't matter what the default packing is.

You can apply the same to DLLs as well, but you should really expect that the calling application has been compiled with a different compiler and not depend on the compilers being the same.

流绪微梦 2024-12-28 15:57:31

所有 Windows API 都会疯狂地抛出结构,因此显然这是每天都会做的事情并且它有效。当然,这并不意味着您的担忧没有根据:)
我建议使您的结构体字段具有显式宽度类型(int32_t 等),并且可能明确指定打包方式会在除您之外的任何编译器上中断,即

#if defined(_MSC_VER)
#pragma pack(0)
#elif defined ... handle gcc
#else
FAIL // fail compilation on unsupported platform
#endif

All Windows APIs throw structs around like crazy so obviously this is something that is done every day and it works. Of course it doesn't mean that your concerns are not valid :)
I would suggest making your structure's fields have explicit width types (int32_t etc) and maybe specify explicitly that that the packing in a way which would break on any compiler but yours, i.e.

#if defined(_MSC_VER)
#pragma pack(0)
#elif defined ... handle gcc
#else
FAIL // fail compilation on unsupported platform
#endif
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文