Boost logo

Boost :

From: Vesa Karvonen (vesa.karvonen_at_[hidden])
Date: 2001-05-26 05:02:25


We (Housemarque, Inc. a small game developer) are seriously considering to
start using Boost in production code. However, there are still a few issues
in Boost that currently either prevent using it or make it unnecessarily
hard or ugly.

One of those issues is the design of <boost/config.hpp>.

This is the "textbook" method of dealing with minor platform differences and
everyone seems to be using it. Can everyone be wrong?

My analysis is that the design of <boost/config.hpp> violates certain
fundamental software engineering principles which make Boost more fragile,
more difficult to maintain and more difficult to use safely. You can find a
description of the principles from the following article:

http://www.objectmentor.com/publications/Principles%20and%20Patterns.PDF

The problem
===========

Consider a situation in which you are concurrently developing on multiple
platforms. (This is what we are doing all the time.) Then consider adding a
new platform or changing the platform definitions of an existing platform.
What happens?

Everything, and I mean everything, recompiles. Isn't it quite absurd that
adding a new platform, which has absolutely nothing to do with previously
existing platforms, means that all code on all existing platforms needs to
be recompiled?

Effectively, there is an imposed physical dependency between platforms that
have nothing to do with each other. Essentially, the traditional solution
employed by <boost/config.hpp> does not conform to the Open-Closed Principle
(OCP):

 "A module should be open for extension but closed for modification."

Extending <boost/config.hpp> implies modifying existing code.

Furthermore, consider the complexity and fragility of the platform detection
code. What if a simple change breaks the detection on some minor platform?
What if someone accidentally or on purpose (as a workaround for some other
problem) defines some platform dependent macros that are used by the
detection code?

The <boost/config.h> is one of the most volatile headers of the entire Boost
repository. More stable elements of Boost depend on it. This violates the
Stable Dependencies Principle (SDP):

 "Depend in the direction of stability."

After even a minor change to <boost/config.h> on one minor platform, almost
everything on every platform should be tested if we follow sound software
engineering practice.

Another important issue is that it is not always possible to submit changes
to <boost/config.hpp>. We have been working, and are currently working, on
platforms using tools and libraries that are under strict NDAs
(Non-Disclosure Agreement). It would have been impossible for me to submit
<boost/config.hpp> for the Dreamcast and it is equally impossible for me to
submit <boost/config.hpp> for the X-Box, which is one of the closed
platforms that we are currently working on.

To make matters even more interesting. The <boost/config.hpp> is
spesifically designed so that it will change in the future, when compiler
vendors come closer to standard C++. (No, I think that the NO convention is
good, I'm using it myself, but I think that this was worth pointing out.)

In summary, <boost/config.hpp> violates fundamental software engineering
principles, most notably:
- The Open-Closed Principle (OCP), and
- The Stable Dependencies Principle (SDP).
This makes Boost much more fragile and difficult to use than it has to be.
In particular, the following effects can be observed:
- It is more difficult to use Boost on platforms that are under NDA.
- <boost/config.hpp> is one of the most volatile headers of boost and causes
unnecessary recompilation and testing of code that shouldn't ideally happen.
- <boost/config.hpp> makes Boost rather fragile, because changing the header
risks breaking code on minor platforms, which may be impossible to test
consistently.

An alternative
==============

In order to break the artificial dependency, the platform dependent
definitions must be placed in physically separate headers. This way you can
safely modify the definitions of an existing platform or add a new platform
without touching any of the code for other existing platforms.

Furthermore, if you name the headers identically, but place them in distinct
platform dependent directories, you can easily include the configuration for
your platform by using platform invariant code simply by adding the platform
dependent directory to the list of include directories for the platform.

In other words, it is possible to use the following file structure:

    boost/config/xbox_msvc/boost/config.h
    boost/config/dreamcast_mwcw/boost/config.h
    boost/config/w32_msvc/boost/config.h
    boost/config/x86_linux_gcc/boost/config.h
    boost/config/.../boost/config.h

Then you must simply add the correct platform configuration directory to the
list of include directories on your platform. For example:

    g++ -Iboost/config/x86_linux_gcc ...

Other Boost code need not be modified. In particular,

    #include <boost/config.h>

Remains exactly the same.

This solution conforms to the OCP (by applying Dependency Inversion
Principle (DIP)). This solution also comes much closer to achieving, or
literally conforms to, the SDP, because the frequency of modifications to
the <boost/config.h> of a particular platform drops by a factor of N, where
N is the amount of distinct platforms.

I independently developed this technique from first principles in 1999 and
we have been using this solution in our own libraries since then with good
results. I recall observing that at least STLPort did something similar at
some point.

A workaround
============

Yes, it is possible, to use the previously explained file structure
technique and replace the common <boost/config.hpp> by making sure that the
compiler looks into our configuration folder first. This is exactly what
we'll have to do anyway, with the important exception that if Boost would
use the file structure technique, the risk of using the wrong configuration
file would be smaller, because there would be no default configuration
header, or the default header would cause either a compilation error or a
warning message.

However, this workaround does not change the fact that <boost/config.hpp>
does not conform to OCP and SDP and does not eliminate the observable
effects caused by the violation on the platforms that are handled by the
standard <boost/config.hpp>.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk