Boost logo

Boost :

From: Joaquín Mª López Muñoz (joaquin_at_[hidden])
Date: 2007-05-09 09:23:27


Gregory Dai ha escrito:

> On 5/8/07, Medland, Thomas M <thomas.medland_at_[hidden]> wrote:
> >
> >
> > Hi,
> >
> > I have just moved company and in my old company I used to use a
> > RogueWave singleton class. I was looking to use the boost singleton
> > class. However after looking at the implementation I saw there was no
> > private default copy constructor or assignment operator. Does this not
> > mean that I could create multiple instances of my singleton via
> > assignment or some other form of copying? I saw some discussion of a
> > better singleton representation in boost and I was wondering what the
> > status of this was, and I was also wondering what other people use for
> > their singletons?
> >
> > Thanks
> > Tom
>
> Well, I use the following. It's pretty simple and it works.

[...]

Hello Greg, I think there's a potential problem with your static_instantiation
policy (or at least a limitation in applicability) due to its relying on the class
template static member static_instantiation::holder<T>::instance_s. As this
member is initialized only once (for a given T), the initialization will take
place in some of the translation units, but there's no way to say which one,
and the standard cannot not guarantee that the initialization will occur
before the first use of static_instantiation::holder<T> if that use happens
at dynamic initialization time.

The following shows the problem:

***foo.hpp***
#include <singleton.hpp>

struct foo
{
  foo():initialized(true){}

  bool initialized;
};

typedef ste::pattern::singleton<
  foo,
  ste::pattern::static_instantiation
> foo_singleton_type;

***tunit1.cpp***
#include "foo.hpp"

bool b1=foo_singleton_type::instance()->initialized;

***tunit2.cpp***
#include "foo.hpp"

bool b2=foo_singleton_type::instance()->initialized;

***main.cpp***
#include <iostream>

extern bool b1;
extern bool b2;

int main()
{
  std::cout<<"b1: "<<b1<<std::endl;
  std::cout<<"b2: "<<b1<<std::endl;
}

******

I've tried the program composed of main.cpp, tunit1.cpp and tunit2.cpp
in GCC 3.2 and the program output is not always what one'd expect,
but rather it depends haphazardly on the order the translation units are
compiled:

  g++ ... main.cpp tunit1.cpp tunit2.cpp
  output:
  b1: 1
  b2: 1

  g++ ... main.cpp tunit2.cpp tunit1.cpp
  output:
  b1: 0
  b2: 0

  g++ ... tunit1.cpp main.cpp tunit2.cpp
  output:
  b1: 1
  b2: 1

  g++ ... tunit1.cpp tunit2.cpp main.cpp
  output:
  b1: 1
  b2: 1

  g++ ... tunit2.cpp main.cpp tunit1.cpp
  output:
  b1: 0
  b2: 0

  g++ ... tunit2.cpp tunit1.cpp main.cpp
  output:
  b1: 0
  b2: 0

You can solve this by combining a Meyers singleton with some static initialization
forcing technique:

class static_instantiation
{
    template <typename T>
    struct init_forcer
    {
      init_forcer()
      {
          T* p;
          static_instantiation::make<T>(p);
      }

      void do_nothing(){}

      static init_forcer<T> init_forcer_s;
    };

protected:
    template <typename T>
    static void make(T*& p)
    {
        init_forcer<T>::init_forcer_s.do_nothing();
        static T instance;
        p = &instance;
    }
};

template <typename T>
typename static_instantiation::init_forcer<T>::init_forcer
static_instantiation::init_forcer<T>::init_forcer::init_forcer_s;

This technique is used for instance by Boost.Pool,
see boost/pool/detail/singleton.hpp. Hope this helps,

Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo







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