Skip to content
Tech News
← Back to articles

C extensions, portability, and alternative compilers

read original get Cython for Python Integration → more articles
Why This Matters

This article highlights the challenges faced by developers working with C extensions, portability, and alternative compilers, emphasizing how reliance on non-standard behaviors and compiler-specific features complicates cross-platform compatibility. It underscores the importance for the tech industry to develop more portable and standardized solutions to improve code reliability and maintainability for consumers and developers alike.

Key Takeaways

On C extensions, portability, and alternative compilers

— c compilers

Anyone who's written C knows that full ISO C standard-adhering code is an impractical rarity. Most real world C code out there relies on non-standard behaviors and language extensions to varying extents, and a lot of this isn't for extra features, but just to work around bugs and gaps in different compilers and libraries. A lot of codebases will try somewhat to support various environments, mostly through the use of preprocessor checks and guards, but these attempts are finicky at best and straight up broken at worst.

I have ran into many of these situations while working on my C compiler, so here's a small list of some of them.

glibc

The system's C library headers is the first 'obstacle' for a C compiler aspiring to be useful. If you can't preprocess and parse <stdio.h> , you won't get past hello world. Because I use GNU/Linux, that means glibc. Now, to their credit, glibc does try to retain compatibility of its headers on non-GCC compilers. In the monstrosity that is sys/cdefs.h, which is indirectly included by every libc header, they use all kinds of preprocessor checks for compiler-predefined macros to determine what kinds of compiler extensions are supported, and #define things away when they aren't.

Unfortunately this is just broken sometimes. For example, on Linux struct epoll_event from sys/epoll.h is a packed struct, which uses the GNU __attribute__((packed)) . Because this changes the struct layout (on 64 bits), you can't ignore it without breaking ABI. So okay, say you implement support for __attribute__((packed)) in your compiler. But this isn't enough, because the aforementioned sys/cdefs.h contains this code:

/* GCC, clang, and compatible compilers have various useful declarations that can be made with the '__attribute__' syntax. All of the ways we use this do fine if they are omitted for compilers that don't understand it. */ #if !(defined __GNUC__ || defined __clang__ || defined __TINYC__) # define __attribute__(xyz) /* Ignore */ #endif

If you aren't gcc, clang, or tcc, tough luck.

Although, the epoll header is Linux-specific, so you could argue that applying C standards portability criteria is not fair.

... continue reading