Building Boost from SVN trunk with Visual Studio 2008 (x86 & x64)

Welcome to the bleeding edge. Just switched to Visual Studio 2008, and you need Boost for your project? Then read on to see how you can build Boost from trunk with VC9 (actually, this also applies to VC8, and possibly even earlier versions, but I just tried with VC9).

Obtaining Boost

First of all, you need to get Boost. A detailed guide is available here, basically all you need to do is a SVN checkout of http://svn.boost.org/svn/boost/trunk. I got revision 42333, which I'll use for the rest of this article.

Building Boost

Boost uses a custom build system, Boost.Build v2. Just take a look at the detailed manual to see how big the system is. Anyway, the first step is to build BJam, which is located in tools/build. Building is straightforward unless you have parentheses in your path - if so, read this earlier post describing the problem. Anyway, at the end, put the bjam.exe into the folder you just checked out from SVN (right to the index.html stuff!). [Update]: For recent Boost builds (I think 1.40 or later), you just have to open a Visual Studio command prompt (or call the vcvars*.bat file located in vc/bin / vc/bin/amd64 folders) and run bootstrap.bat, which will generate bjam right in the root.

Running Boost.Build

For this example, I'll assume you want to build the static-link libraries, x64, debug and release versions, and omit some libraries like Wave and Python to make things more interesting. Let's start like most programming examples, by looking right at the command line needed:

bjam --build-dir=..\\tmp\\build
--prefix=..\\install-trunk link=static,shared debug release
address-model=64 threading=multi --toolset=msvc-9.0 --without-python
--without-mpi install
  • build-dir (not builddir as bjam --help says!) sets the build directory where all the temporary files will end up. You do not want to have them in your source folder, so I simply moved them one folder up.
  • prefix is the folder where the libraries and include files will be copied to eventually.
  • link specifies the linkage of the files. I use static as it simplifies deployment and works nicely with the auto-link stuff and shared (which results in DLLs) just for this example ;)
  • Next the targets, debug and release.
  • address-model=64 tells BJam to use the x64 compiler. Otherwise, even if you run the x64 command prompt, it will generate x86 binaries. If you want x86 binaries, specify address-model=32.
  • threading=multi tells the compiler to link against the multi threaded-CRT -- this is actually no issue for VC9 as there is no single-threaded CRT but I added it here just to be sure.
  • The toolset, to avoid guesswork, I specified the full compiler version.
  • --without-name disables building some libraries.
  • install tells BJam to copy the files to the prefix folder. This generates a folder called libs where the libraries end up and a folder include with the include files.

Those options are also explained in the builtin features section of the manual. You need to start this from the Visual C++ Command prompt -- verify that you are running it by typing cl, it should give you something like:

Microsoft (R) C/C++ Optimizing Compiler
Version 15.00.21022.08 for x64 Copyright (C) Microsoft Corporation. All

rights reserved.

Well, that's it basically, now you need some patience, and you should get a perfect working version of Boost tailored at your needs with just the libraries you want. For the sake of completeness, here the command line I use to build Boost:

bjam --build-dir=..\\tmp\\build
--prefix=..\\install-trunk-x64 link=static debug release
address-model=64 threading=multi --toolset=msvc-9.0 --with-system
--with-filesystem --with-thread --with-date\_time
--with-program\_options --with-signals install

[Update] Reformatted the command lines, and updated my build command line to use the --with-library commands instead of the --without.

C++ background: Static, reinterpret and C-Style casts

Ever wondered why C-Style casts and reinterpret_cast casts are considered evil? Let's take an in-depth look at what goes wrong with them.

Background

C++ knows 5 different casts (yeah, C-Style casting is not reinterpret_cast):

  • static_cast: Least harmful, can downcast pointers
  • const_cast: Removes the const modifier. If used incorrectly, this can be a killer as the target might be really const and you get some invalid access errors.
  • dynamic_cast: Safe down/cross-casting between classes, requires RTTI - and RTTI in C++ is something that is often not enabled at all.
  • reinterpret_cast: Casts anything which has the same size, for example, int to FancyClass* on x86. Now this is not really a cast any more but just a way to tell the compiler to throw away type information and treat the data differently.
  • C-Style casting, using the (type)variable syntax. The worst ever invented. This tries to do the following casts, in this order: (see also C++ Standard, 5.4 expr.cast paragraph 5)

    1. const_cast
    2. static_cast
    3. static_cast followed by const_cast
    4. reinterpret_cast
    5. reinterpret_castfollowed by const_cast

    And you thought it is just a single evil cast, in fact its a hydra!

The rule of the thumb should be: Never use reinterpret_cast or C-Style casting, if you need to cast pointers, cast them via void*, and only if absolutely necessary use reinterpret_cast - that means, if you really have to reinterpret the data. Remember, C++ is an expert language, it gives you all the control over your machine you wish, but with power comes responsibility!

Example

Let's take a look why the "hard" casts are really evil and should be avoided like the plague. In this example, we'll assume the following class hierarchy:

class ParentWithoutVtable
{    
    char empty;
};

class ParentWithVTable
{
    public:
        virtual ~ParentWithVTable () { }
};

class Derived : public ParentWithoutVtable, public ParentWithVTable
{
};

Now, consider this code:

int main () {
    ParentWithoutVtable* p = new Derived ();
    Derived* a = reinterpret_cast<Derived*>(p); // (1)
    Derived* b = static_cast<Derived*>(p); // (2)
    Derived* c = (Derived*)(p); // (3)
}

What happens? Well, 1 will fail miserably, 2 will work and 3 - depends on whether you included the header properly or just forward defined the types! Lets take a look at (1) first: It fails because the ParentWitoutVTable* pointer does not point at the beginning of the newly created Derived object in memory, but somewhere else. For example, Visual C++ puts the virtual table pointer as the first element of a class, so the real layout of Derived is something like:

struct __Derived
{
    function_ptr vtable; char empty;
}

So if we obtain a pointer to an object without a vtable, it will point at empty, otherwise access via the pointer would fail. Now, the reinterpret_cast has no idea of this, and if you call a Derived function, it will think the empty is the vtable. The static_cast does it right, but for this it has to know the full declaration of both types. This is exactly where the C-Style is dangerous: As long as the full declaration is available, everything will work fine without any warning, but if you choose to forward-declare the types, the C-Style will still not emit a warning (while the static_cast will fail!) and perform a reinterpret_cast which will break your code. So be aware, and avoid C-Style casting at all costs.

[Update] Changing the order in which Derived inherits from its parents does not solve this problem. The C++ standard makes no guarantees how the members will be layed out:

the order of derivation is not significant except as specified by the semantics of initialization by constructor (12.6.2), cleanup (12.4), and storage layout (9.2, 11.1).

and the referenced paragraph says:

Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

Basically, the compiler is free to put the virtual function table where he wants -- Visual C++ for example always stores the virtual function table first, no matter in what order the class derives from its parents.

Netbeans and SVN over SSH

If you are using Netbeans and you connect to your SVN server over SSH, you'll get some "file not found error" if you try to access it from Netbeans. In order to get it working, you need to have an application installed to create the SSH tunnel, like TortoisePlink which comes with TortoiseSVN. You have to set up an environment variable "SVN_SSH" with escaped backslashes pointing to it, for example by using the command line: > set SVN_SSH=C:\\Program Files\\TortoiseSVN\\bin\\TortoisePlink.exe

Competition comeback

After the G80, AMD's graphics group (formerly known as ATi) seemed to have no chance to catch up again with nVidia. Well, having learned from their mistakes, they just released a great midrange part - the HD 3800 series. This is a direct competition for the 8800GT cards, and with the latter having availability issues, AMD has a great chance to get back into the market. The midrange situation is as good as never before, I don't remember when cards around 220$ offered so much power compared to the high-end. Really a good time right now - thanks for remaining competitive, AMD! I can just hope the Phenoms will do the same for the CPU market.