Search code examples
cshared-librariesportability

C portable shared library interface: best practice on primitive types


Problem:

Recently we've encountered the following problem with our C shared library.
The library defines a method like this one:

typedef enum {A, B, C} some_enum;
typedef struct {some_enum e; time_t t; char* data;} request;
void f(request);

We've compiled the library for multiple architectures using clang and its cross-compilation features. One of the platforms was Windows 32 bits.
And here is the problem: when trying to use the library in an example that uses MSVC 32-bit compiler, the example fails with seg fault.
The reason: in MSVC 32-bit time_t is 8 bytes while in clang compilation for Windows 32-bits it is assumed to be 4 bytes.

Obviously, if we were using fixed-width integer types such as int64_t, this problem would never appear.

Question:

Are there established best practices for primitive types in a portable C shared library?

For instance, is it the best practice to avoid any non fixed-width integer types altogether in C shared library interfaces?
Are for example enums "allowed" (in terms of best practices) in a portable C library?


Solution

  • Typically all compilers for a given platform try very hard to preserve the default C ABI. Violation of ABI is normally considered a compiler bug.

    C++ ABI is trickier for various reasons but at least Clang tries hard to preserve that one on Windows as well.

    C ABI compatibility means, among other things, that all primitive types have defined size and alignment so there is no need to use fixed-width types (i.e. long will be the same for all compilers for a particular target).

    As for your case, I suspect that clang and cl.exe are using different time.h for whatever reason so I suggest to look into that (see my comment above on how to proceed with this).