1. 4
  1.  

  2. 3

    People underestimate just how different systems used to be. Endianness, character encodings, different sizes for int, sure…but also things like “oh yeah, on this variant of Unix the third argument to lseek is broken.”

    1. 2

      At one point the authors talk about moving the system-specific stuff into their own files, hidden behind an interface. They say,

      However, in our experience, it’s much more usual for the different variants to be completely different code, compiled from different source files—in essence, the parts outside the #ifdef disappear.

      This is true, but what they don’t mention is now the “#ifdef” part becomes part of the build system. The logic of the variants doesn’t go away, it now gets moved elsewhere.

      1. 4

        This gets very difficult with C once you realise that you don’t have completely different systems, you have overlapping sets of functionality. You end up needing a generic POSIX implementation and then a bunch of specialised ones.

        This is one of the reasons that I much prefer C++ over C. I can write a templated class that uses a set of mostly portable APIs, with the template parameterised on some per-platform tweaks and a per-platform subclass can inherit from the templated class and then export the platform-specific type as the default implementation.

        For example, in snmalloc we have a POSIX implementation of our platform-abstraction layer (PAL) that should work on pretty much any *NIX. There’s a BSD subclass that uses a generic BSD extension and another subclass of this for members of the BSD family that provide strongly aligned allocation from mmap. The FreeBSD and NetBSD implementations are identical: we only maintain them as separate things so that there’s an extension point if we want to add platform-specific behaviour. Some systems, such as Haiku are a bit more custom, but the amount of code is fairly small. The only #ifdefs are for selecting which PAL to expose as the DefaultPAL type.

        All of the rest of the code that cares about the platform takes the PAL as a template parameter. This means that we can test the POSIX PAL on any POSIX platform and make sure that we haven’t broken it. We can also provide custom ones (e.g. for OpenEnclave).

        There’s no dynamic dispatch for any of this and the PAL functions are typically inlined everwhere that they’re used.

        Doing the same in C is a complete mess.

      2. 1

        In the world where that sort of genericity is required… here is a masterclass in how to do it…

        https://github.com/dlang/phobos/blob/master/std/experimental/checkedint.d