C++ Boost

Serialization

singleton


Motivation
Features
Class Interface
Requirements
Examples
Multi-Threading

Motivation

The serialization library relies on the existence of a number of static variables and tables to store information related to runtime types. Examples are tables which relate exported names to types and tables which relate base classes to derived classes. Construction, destruction and usage of these variables requires consideration of the following issues: This singleton class addresses all of the above issues.

Features

This singleton implementation has the following features:

Class Interface


namespace boost { 
namespace serialization {

template <class T>
class singleton : public boost::noncopyable
{
public:
    static const T & get_const_instance();
    static T & get_mutable_instance();
    static bool is_destroyed();
};

} // namespace serialization 
} // namespace boost


static const T & get_const_instance();

Retrieve a constant reference to the singleton for this type.


static T & get_mutable_instance();

Retrieve a mutable reference to the singleton for this type.


static bool is_destroyed();

Return true if the destructor on this singleton has been called. Otherwise, return false.

Requirements

In order to be used as singleton<T> , the type T must be default constructible. It doesn't require static variables - though it may have them. Since the library guarantees that only one instance of singleton<T> exists and all access is through the above static interface functions, common member functions of T become the functional equivalent of static functions.

Examples

There are at least two different ways to use this class template. Both are used in the serialization library.

The first way is illustrated by an excerpt from the file extended_type_info.cpp. which contains the following code:


typedef std::set<const extended_type_info *, key_compare> ktmap;
...
void
extended_type_info::key_register(const char *key) {
    ...
    result = singleton<ktmap>::get_mutable_instance().insert(this);
    ...
}
Just by referring to the singleton instance anywhere in the program will guarantee that one and only one instance for the specified type (ktmap in this example) will exist throughout the program. There is no need for any other declaration or definition.

A second way is to use singleton<T> as one of the base classes of the type. This is illustrated by a simplified excerpt from extended_type_info_typeid.hpp


template<class T>
class extended_type_info_typeid : 
    public detail::extended_type_info_typeid_0,
    public singleton<extended_type_info_typeid<const T> >
{
    friend class singleton<extended_type_info_typeid<const T> >;
private:
    // private constructor to inhibit any existence other than the 
    // static one.  Note: not all compilers support this !!!
    extended_type_info_typeid() :
        detail::extended_type_info_typeid_0()
    {
        type_register(typeid(T));
    }
    ~extended_type_info_typeid(){}
    ...
};
This usage will permit a more natural syntax to be used:

extended_type_info_typeid<T>::get_const_instance()
Again, including one or more of the above statements anywhere in the program will guarantee that one and only one instance is created and referred to.

Multi-Threading

This singleton CAN be safely used in multi-threading applications if one is careful follow a simple rule:

Do not call get_mutable_instance when more than one thread is running! All singletons used in the serialization library follow this rule. In order to help detect accidental violations of this rule there exist singleton lock/unlock functions.


void boost::serialization::singleton_module::lock();
void boost::serialization::singleton_module::unlock();
bool boost::serialization::singleton_module::is_locked();
In a program compiled for debug, any invocation of get_mutable_instance() while the library is in a "locked" state will trap in an assertion. The singleton module lock state is initialized as "unlocked" to permit alteration of static variables before main is called. The lock() and unlock() are "global" in that they affect ALL the singletons defined by this template. All serialization tests invoke lock() at the start of the program. For programs compiled in release mode these functions have no effect.

© Copyright Robert Ramey 2007. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)