diff --git a/docs/SystemLibrary.html b/docs/SystemLibrary.html index f67b8b459ee..5e5bd8a1217 100644 --- a/docs/SystemLibrary.html +++ b/docs/SystemLibrary.html @@ -174,6 +174,118 @@ for further details on the progress of this work

+ +
Rationale For #include Hierarchy +
+
+

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:

+
    +
  1. Provide the compiler with a -I$(OS) on the command line. This could be + provided in only the lib/System makefile.
  2. +
  3. Use autoconf to transform #include statements in the implementation + files by using substitutions of @OS@. For example, if we had a file, + File.cpp.in, that contained "#include <@OS@/File.cpp>" this would get + transformed to "#include <actual/File.cpp>" where "actual" is the + actual name of the operating system
  4. +
  5. Create a link from $OBJ_DIR/platform to $SRC_DIR/$OS. This allows us to + use a generic directory name to get the correct platform, as in #include + <platform/File.cpp>
  6. +
+

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:

+

+
+
Reference Implementation