Portability problems can be seen from two points of view: the operating system and the architecture. Depending on the kind of application you are developing, you may hit these problems. An example of OS-portability can be the use of a specific hardware subsystem through kernel facilities; on the other hand, an example of architecture-portability can be the direct use of assembly code.

Often, you will be aware that a chunk of your code is not portable; for example, when accessing Linux's ACPI subsystem, you can be sure that that part of the code won't work outside Linux.

Some people will do an extra research effort and see how other systems do what they need, and will add the right code to make their program portable. Unfortunately, many people opts to keep the code as is within the program.; that is, it gets compiled unconditionally, thus causing build failures on any other OS. And a wrong desing at this point will make your code unreadable when adding portability workarounds.

In this situation, you can mitigate the problem by isolating the unportable code, even if you won't be implementing support for other platforms. This way, anyone who builds the package will quickly notice why it fails, and he may be able to provide a better patch with less hassle. How to do it is up to you: either create a source file for each platform you want to support, use preprocessor conditionals or use inheritance in an OOP language. Whichever case you choose, keep scalability in mind: adding support for another OS/architecture should be easy (I'm referring to the code structure, not to how easy is to port the feature).

Resuming the previous ACPI example, you could organize your code by using a file per OS, like in: acpi-linux.c, acpi-netbsd.c, etc. Or you could do something like:

/* Prototypes for portability functions defined below. */
static void _acpi_init(void);

/* Public functions that are called from other .c files. */
void
acpi_init(void) {
...
_acpi_init();
...
}

#ifdef __linux__
/* Inclusion of Linux-specific headers */
/* Private functions to use Linux's ACPI */
static void
_acpi_init(void) { ... }
#elifdef __NetBSD__
/* Inclusion of NetBSD-specific headers */
/* Private functions to use NetBSD's ACPI */
static void
_acpi_init(void) { ... }
#else
# error "Sorry, this code has not been ported yet."
#endif

As you can see, the "generic" code is kept simple: it calls the system specific function and does any other common stuff; no conditionals are inserted there to handle OS details. Moreover pay special attention to the #error directive: it will tell the preson building your software why it failed, and won't lead him to confusion.

These comments come from lots of portability issues I've hit while making some programs work under NetBSD; fortunately, I'm not using anything else than i386. Hope you find these suggestions helpful and apply them to your code; they'd make the task of packagers a bit easier. Ah, and remember: the world is not Linux/i386 only!

Comments from the original Blogger-hosted post: