Some thoughts about portability
I was reading through Kernigan and Pike's
The
Practice of Programming this weekend. It's a good book-- although,
to be honest, I was already aware of a lot of the things they talked
about.
One little gem I found in the book was a comment about portability. As
the book points out, there are two ways you can write portable code,
which they call the
"union" approach and the
"intersection" approach.
The union approach is to write compatibility shims over the
platform-specific interfaces that each platform gives you. The
intersection approach is to use only APIs that are provided by all the
platforms you intend to support.
A lot of programmers intuitively reach for the union approach. The
platform-specific APIs are often nicer and more powerful than the
cross-platform ones. However, Kernighan and Pike favor the
intersection approach. As they point out, it results in fewer lines of
code, which translates into less that you need to test.
I have to admit, they are probably right. If you can, use the
intersection approach to portability, and save yourself a lot of grief
down the road.
The Third Way
Another approach is to use a library which hides the platform-specific
stuff from you. One example of this is
libev. Basically,
if you have to deal with lots and lots of file descriptors in C, libev
is what you want to use. Unlike epoll, which is Linux-specific, or
kqueue, which is BSD-specific, libev works across many different
platforms.
Of course, you have to be judicious about adding a new library to your
project. In general, I tend to reject libraries that introduce a huge
set of unecessary dependencies, are inefficient, or try to solve too
many unrelated problems. The ideal library just does one thing, and
does it very well.
Even well-written libraries have limitations. For example, libev
doesn't perform very well on Microsoft Windows. This is not really the
fault of the author, but just a reflection of the fact that file
descriptors are second-class citizens on Windows. In order to get
optimal performance on Windows, you would have to use completion ports,
which have a totally different API.
So in summary: don't be too quick to write compatibility shims. First,
see if you can make do with the APIs that are available to you on every
platform. If you do decide you need the shims after all, see if anyone
else has produced a good library to fill that role.