Hacker News
C extensions, portability, and alternative compilers
whizzter
|next
[-]
The article highlights a typical piece:
#if !(defined __GNUC__ || defined __clang__ || defined __TINYC__)
# define __attribute__(xyz) /* Ignore */
#endif
There is no reason that !defined check to not include a check for __attribute__ already being defined (a custom compiler author could then force an define for __attribute__ that translates to an internal __mycompiler__attribute__ replacement by default).But outside of that, just trying to compile on FreeBSD you often run into systemd dependencies or other non-posix behaviors (Not to mention on Windows but I'm not here to bring on flamewars so I'll leave that part).
rwmj
|root
|parent
|next
[-]
Surprised about FreeBSD. My experience is that porting Linux software is usually pretty easy as long as it's not using some Linux-only feature (io_uring for instance).
kps
|root
|parent
|next
|previous
[-]
“All the world's a VAX”
https://groups.google.com/g/comp.lang.c/c/CYgWkWdWCcQ/m/thMt...
BadBadJellyBean
|root
|parent
|next
|previous
[-]
dooglius
|root
|parent
|next
|previous
[-]
einpoklum
|root
|parent
|next
|previous
[-]
That said - I think a rule-of-thumb one can follow is that any inclusion of a file with a directory prefix, especially `<sys/whatever>`, needs a guarantee-of-availability in your build configuration phase, e.g. CMake `find_package()`, or or at least `check_include_file()` and such. That way, you might be more likely to fail to build, but at least you'll be telling the user "I expect these things to be present".
formerly_proven
|root
|parent
|previous
[-]
whizzter
|root
|parent
|next
[-]
2: You could easily compile Samba yourself for FreeBSD in the past, last time I tried a new version it broke in what I remember being due to linux-isms (yes there is ports, but being reliant on older versions if ports maintainers can't keep up isn't a good thing).
3: The only "fairly basic" stuff that's hugely different is mostly the absence/reliance on shell-scripts (when building), but that has little to do with the actual code function (Personally I often used Node scripts in those scenarios, Python scripts would probably be an improvement since there's no reason it couldn't be everywhere).
I used to use Tremor to decode Ogg audio (no UI needs, just binary data in, arrays of primitive values in audio buffers out), early versions were easy to compile under Windows but building later versions were buried in shell scripts generating headers,etc for no real good reason (maybe to help port when working on a Linux workstation to other embedded devices but made the code less easily compilable by default), the core functionality only really needed a C compiler as early versions showed.
I can agree that something with advanced UI's like Blender (that relies on GL/3d rendering for UI) might not be easily portable, but when algortihm libraries often requires heavy reworking it's not a good thing (Here I think Github has helped since people has had an easier time to contribute, it's a sad thing that people are moving away due to the AI-crap).
In the end, it's not about _actual_ differences but more of a superiority complex of Linux users that is the main roadblock.
WalterBright
|next
|previous
[-]
https://github.com/dlang/dmd/blob/master/druntime/src/import...
https://github.com/dlang/dmd/blob/master/druntime/src/__impo...
fuhsnn
|next
|previous
[-]
[2] https://github.com/fuhsnn/slimcc/blob/main/slimcc_headers/pl...
Some more fun stories:
- Game projects default to using SIMD so for example SDL and STB you always need to pass -DSDL_DISABLE_IMMINTRIN_H and -DSTB_NO_SIMD
- math.h's NAN usually fall back to (0.0f / 0.0f), which will print "-nan" with printf, some projects test suite fail because of it (they expected "nan").
- NetBSD's sys/cdefs.h straight up #error's if you don't pretend to be GCC or PCC.
- Some projects can't compile without __attribute__((always_inline)) because they use it on non-static functions.
- Many projects probe -fvisibility in the build system and pass -fvisibility=hidden to compile, but in the headers they gate __attribute__((visibility(default))) behind __GNUC__ checks, so you'll get missing symbols.
- Some projects use if(0) { undefined_function() } to fake static_assert(), there is even a bug report from QEMU to Clang because it failed to optimize in -O0 a certain `if` written this way.
- Even if you define __STDC_NO_VLA__, projects might fall back to alloca() code path that's untested and broken (python and jemalloc both had this problem, already reported)
- Valkey has broken __builtin_ctzll fallback nobody noticed (reported).
- Zig's C bootstrap path expects the compiler to have GCC/Clang-tier optimization and stack overflows if you don't (reported).
- I contributed stdatomic.h code path for Ruby just to compile it with slimcc, pretty sure it's still the only user of the code path.
- I implemented __has_extension in the hope that projects can use it to query gnu_asm; but SQLite broke because they use __has_extension(c_atomic) to query GNU atomics builtin, but c_atomic actually is meant for C11 _Atomic (IMO they should use __has_builtin)
meghprkh
|next
|previous
[-]
+ #if !(defined __GLIBC_COMPILER_SUPPORTS_ATTRIBUTES__)
- #if !(defined __GNUC__ || defined __clang__ || defined __TINYC__)
# define __attribute__(xyz) /* Ignore */
#endif
(or probably a more fine grained for each attribute they try to use)Considering such checks are fairly conventional in downstream C++ libraries based on compilers (for example checking OS platform or compiler, e.g. [Boost.Config](https://www.boost.org/doc/libs/latest/libs/config/). Modern C++ even went ahead and standardized this somewhat https://en.cppreference.com/cpp/utility/feature_test )
ventana
|root
|parent
[-]
It's probably just natural for software developers (myself included), whenever only FooZoid v5 supports frobnicate(), say "#ifdef FOOZOID_V5" and go back to your business, rather than introducing "FROBNICATE_SUPPORTED".
Also, when you try to ask for a feature flag in the code review, people will throw YAGNI at you, and they might be not wrong, at least for the first few years. After that, it's a costly refactor.
rurban
|next
|previous
[-]
lelanthran
|root
|parent
[-]
Dealing with the preprocessor (essentially a different, crippled language) was too much headache at the start so I just used `cpp`, and at the end, I was just too lazy to implement it, so continued using `cpp`.
(BTW: Anyone else here ever used the Cosmic C Compiler for Motorola microcontrollers? Amongst other idiosyncrasies, it had only one datatype - `byte` - and I had to implement macros to do 16-bit arithmetic operations. That project was easily the worst development experience of my life.)
einpoklum
|next
|previous
[-]
I've written quite a bit of C code and do not know that to be a rarity. Especially when it comes to libraries rather than applications, and FOSS as opposed to proprietary code.
> Most real world C code out there relies on non-standard behaviors and language extensions to varying extents
Maybe it depends on which domain you're working in. At companies whose target platform is not a PC, relying on idiosycratic behavior, or extensions, is difficult: The compiler for the target device may simply not support the bells and whistles of GCC or whatever, so you stick to C99, (or even C89, ugh) to be on the safe side. And even then there will be things which are standard, but... well, I would be wary of relying on them being supported robustly enough, e.g. variable-length arrays.
And of course, once your code does not target just one single machine then you're forced to have to worry about portability and standard compliance etc.
gritzko
|next
|previous
[-]
btrettel
|root
|parent
|next
[-]
make FC=ifx check
On my Fortran projects, that will run the tests with Intel's Fortran compiler. The Makefile has logic to automatically change compiler flags as appropriate. I default to the GNU Fortran compiler, so `FC` isn't required.I have made a script to run through a series of compilers by alternating between `make check` and `make clean`.
I have separate Makefiles for GNU Make and NMAKE/jom. My Fortran code works fine on various Linux distributions and Windows, though I'll add that achieving that is probably easier with Fortran than C. I've also tried a BSD Make that worked (on Ubuntu at least). My Makefiles are pretty close to the intersection of POSIX and NMAKE, so the main differences between the different Make versions are the conditional statements needed to handle the different compiler flags and the include statements (as I put the compiler flags in separate files).