Purpose

From time to time we have to use objects with static storage duration. The most famous pattern Singleton is often implemented this way. Sometimes static objects should be grouped together to form a container of related objects. The static_list class template is served for this purpose.

The problem

Imagine that you are responsible for implementation of several spell checkers for your favorite open-source e-mail client. After reading a book about object oriented design you decided to define abstract SpellChecker class and special make-function which creates instance of concrete spell checker class for a given language:

class SpellChecker {
  public:
    virtual void checkSpelling(class Text&) = 0;
    virtual ~SpellChecker() = 0;
    // ...
};

std::auto_ptr<SpellChecker> createSpellChecker(const std::string& language);

The design looks good. User of this code will be happy. All she needs is to call createSpellChecker with required language as a parameter.

There is also other group of users you should care of. These are developers who will implement concrete spell checker classes. Although these classes can be developed independently their all should be connected with createSpellChecker function. Some mechanism to register make-function for a given language is needed. Additionally, it would be nice to have container-like interface to access registered entities.

The solution

First of all, lets define a type holding language name and a pointer to correspondent make-function:

typedef SpellChecker* (*MakeFunction)();
typedef std::pair<char const*,MakeFunction> NamedMakeFunction;

Every concrete class derived from SpellChecker shall let others know about its make-function by somehow registering a NamedMakeFunction object. Details followed.

Now, let's turn our attention to static_list class template. It is used to insert elements during static initialization and to access elements afterwards. In our case, access to NamedMakeFunction objects can be made through this type:

typedef static_list<NamedMakeFunction> MakeFunctionList;

Insertion of new object is very simple. All you need is to define special object with static storage duration. Exactly one object for one element in the list. For example, if you have implemented createSpellChecker_en make-function you could just insert the following code into a source file below the function definition:

namespace {
  MakeFunctionList::insert element(NamedMakeFunction("en", createSpellChecker_en));
}

This code inserts an element into MakeFunctionList during dynamic phase of static initialization. When static initialization is completed everyone has access to your element through iterators.

Like other containers MakeFunctionList has begin and end functions to get the range of elements. Unlike others these functions are static members of static_list because there is only one list of NamedMakeFunction elements in a program. Therefore, a range of elements can be represented as shown in a following code snipset:

int size = std::distance(MakeFunctionList::begin(), MakeFunctionList::end()); // range
assert(MakeFunctionList::size() == size); // size() function is faster then distance

For your convenience, static_list has also classic interface:

MakeFunctionList l;
int size = std::distance(l.begin(), l.end());

Copy constructor for static_list is also provided although it doesn't copy list elements. All static_list objects refer to one list. It's worth to note that static_list gives you read-only access to the elements using forward iterator. Consider copying the content into other data structures should you need more advanced access.

Taking all this in mind, createSpellChecker function could be written as follow:

std::auto_ptr<SpellChecker> createSpellChecker(const std::string& language)
{
    MakeFunctionList l;
    for(MakeFunctionList::const_iterator it = l.begin(); it != l.end(); ++it)
    {
        NamedMakeFunction const& element = *it; read-only
        if(element.first == language)
        {
            MakeFunction makeFunc = element.second;
            return std::auto_ptr<SpellChecker>(makeFunc());
        }
    }
    return std::auto_ptr<SpellChecker>();
}

Implementation of a function returning a list of languages with spell checking available is left as exercise for a reader.


Copyright (C) Alexander Nasonov, 2003