Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2005-06-20 08:10:11


I just tried to compile something with the test library and two
translation units. Boom! It was this definition, which appears in
boost/test/impl/execution_monitor.ipp.

  signal_handler* signal_handler::s_active_handler = NULL; //!! need to be placed in thread specific storage

I can hardly blame Gennadiy, though, having made this mistake myself
many times.

The reason I noticed this was that I have just started a simple
practice that should root out these problems in my own code:

  I build a program that #includes all my headers in two translation
  units.

For example:

    // ---- main.cpp ----
    #include <boost/python.hpp>
    extern int f();
    int main() { return f(); }

    // ---- main.cpp ----
    #include <boost/python.hpp>
    int f() { return 0; }

Presto! Instant linker errors pointing me at offending definitions.
Every library should include a test like the one above.

You may be wondering how to work around these issues when you really
need header-only capability and can't stick the definition in a source
file. In this case, the fix was easy: since the variable in question
is a static member, just refactor it into a templated base class.

  class signal_handler;

  template <int = 0>
  struct signal_handler_
  {
      static signal_handler* s_active_handler;
  };

  class signal_handler : signal_handler_<> {
     ...
  };

  //!! need to be placed in thread specific storage
  template <int N>
  signal_handler* signal_handler_<N>::s_active_handler = NULL;

When you need a namespace-scope variable, things get a bit more
interesting. You might consider sticking one in an unnamed namespace,
but then you run a great risk of violating the One Definition Rule
(see 3.2 in the standard) when identical template specializations and
inline functions that use the variable are instantiated and use
distinct objects in two translation units. Boost.Bind's placeholders
have this problem. The answer is to initialize a *reference* in the
unnamed namespace to a single, common object:

  namespace whatever
  {
    class foo {};

    template <class T, int = 0>
    struct unique_instance
    {
        T& get()
        {
            static T x;
            return x;
        };
    };

    namespace
    {
      foo& bar = unique_instance<foo>::get();
    }
  }

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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