From 95189201d1d69f56a900fcb550efa13cfc4f90f5 Mon Sep 17 00:00:00 2001
From: Reid Spencer
In order to provide different implementations of the lib/System interface + for different platforms, it is necessary for the library to "sense" which + operating system is being compiled for and conditionally compile only the + applicabe parts of the library. While several operating system wrapper + libraries (e.g. APR, ACE) choose to use #ifdef preprocessor statements in + combination with autoconf variable (HAVE_* family), lib/System chooses an + alternate strategy.
+
To put it succinctly, the lib/System strategy has traded "#ifdef hell" for + "#include hell". That is, a given implementation file defines one or more + functions for a particular operating system variant. The functions defined in + that file have no #ifdef's to disambiguate the platform since the file is only + compiled on one kind of platform. While this leads to the same function being + imlemented differently in different files, it is our contention that this + leads to better maintenance and easier portability.
+For example, consider a function having different implementations on a + variety of platforms. Many wrapper libraries choose to deal with the different + implementations by using #ifdef, like this:
++ void SomeFunction(void) { + #if defined __LINUX + // .. Linux implementation + #elif defined __WIN32 + // .. Win32 implementation + #elif defined __SunOS + // .. SunOS implementation + #else + #warning "Don't know how to implement SomeFunction on this platform" + #endif + } ++
The problem with this is that its very messy to read, especially as the + number of operating systems and their variants grow. The above example is + actually tame compared to what can happen when the implementation depends on + specific flavors and versions of the operating system. In that case you end up + with multiple levels of nested #if statements. This is what we mean by "#ifdef + hell".
+To avoid the situation above, we've choosen to locate all functions for a + given implementation file for a specific operating system into one place. This + has the following advantages:
+
So, given that we have decided to use #include instead of #if to provide + platform specific implementations, there are actually three ways we can go + about doing this. None of them are perfect, but we believe we've chosen the + lesser of the three evils. Given that there is a variable named $OS which + names the platform for which we must build, here's a summary of the three + approaches we could use to determine the correct directory:
+Let's look at the pitfalls of each approach.
+In approach #1, we end up with some confusion as to what gets included. + Suppose we have lib/System/File.cpp that includes just File.cpp to get the + platform specific part of the implementation. In this case, the include + directive with the <> syntax will include the right file but the include + directive with the "" syntax will recursively include the same file, + lib/System/File.cpp. In the case of #include <File.cpp>, the -I options + to the compiler are searched first so it works. But in the #include "File.cpp" + case, the current directory is searched first. Furthermore, in both cases, + neither include directive documents which File.cpp is getting included.
+In approach #2, we have the problem of needing to reconfigure repeatedly. + Developer's generally hate that and we don't want lib/System to be a thorn in + everyone's side because it will constantly need updating as operating systems + change and as new operating systems are added. The problem occurs when a new + implementation file is added to the library. First of all, you have to add a + file with the .in suffix, then you have to add that file name to the list of + configurable files in the autoconf/configure.ac file, then you have to run + AutoRegen.sh to rebuild the configure script, then you have to run the + configure script. This is deemed to be a pretty large hassle.
+In approach #3, we have the problem that not all platforms support links. + Fortunately the autoconf macro used to create the link can compensate for + this. If a link can't be made, the configure script will copy the correct + directory from $BUILD_SRC_DIR to $BUILD_OBJ_DIR under the new name. The only + problem with this is that if a copy is made, the copy doesn't get updated if + the programmer adds or modifies files in the $BUILD_SRC_DIR. A reconfigure or + manual copying is needed to get things to compile.
+
The approach we have taken in lib/System is #3. Here's why:
+