Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Self-Registering Objects in C++


All functions in Listing Four are completely defined. The proxy class does not need to be modified for each class that it represents. Both GetExtension() and IsCompressed() relegate their work to the original class. Therefore, the encapsulation is complete. Only class T knows what file extension goes with a particular kind of file type, or if that file type is compressed. (I'll ignore the case where one file type has many extensions. GetExtension() could just as easily have been GetListOfExtensions(), but doing so would have complicated this example.)

The Specialty Store

The design of the specialty store is straightforward. The complete source can be found in class FileConverterStore in Store.h. There is one function named Register() that is called by the constructor for FileConverterProxyBase. There are also several functions that comprise the public interface to the specialty store. These functions expose the available criteria for the registered objects.

The specialty store class has two important restrictions. First, a specialty store must be a global variable so that the proxy classes can find it during program startup. Second, neither the specialty store nor any of its members can have any constructors that initialize member variables. Instead, the specialty store must rely on the linker/loader setting the contents to zero. This restriction arises because the ordering of construction of global objects in C++ is not guaranteed. Because proxy classes are registered during the constructors of global variables, Register() can be called by those constructors before the constructor for the specialty store is called. A constructor for the specialty store could end up writing over entries that had already been legally added to the list. I'll work around this problem by relying on a pointer being set to NULL at load time, then Register will allocate the collection class the first time that it is called.

Putting it all Together

Using a specialty store is easy. Back in Listing Two, I showed a typical implementation of file converters that uses a single abstract base class with one derived class for each converter. With a little extra work, we can add support for a specialty store.

First, you use FileConverterStore gConverterStore; to declare the specialty store as a global variable in your application program. Next, you add support for the criteria functions to each file converter class, as in the class definition in Listing Five. Finally, to register each file converter class with the specialty store, you declare a single instance of the proxy class in its implementation file as shown at the end of Listing Five.

Listing Five

class TiffFileConverter : public FileConverter{
public:
    virtual void Import(const char *filename);
    virtual void Export(const char *filename);
    // These are the criteria support functions
    static char* GetExtension()
        { return ".tif"; }
    static bool IsCompressed()
        { return TRUE; }
};
FileConverterProxy<TiffFileConverter> gTiffProxy;

This declaration is what makes a self-registering object. By including it in the implementation file for TiffFileConverter, the class automatically becomes available at run time without making any changes to any other sources. If necessary, the constructor for FileConverterProxy could be modified to decide at run time whether or not the class should be registered. For example, in the case of TiffFileConverter, a license file could be used to determine if the class should be registered.

Opening for Business

The specialty store can be used in two ways. In the first, the store returns an object based on some criteria. The CreateByExtension() member function creates a conversion object to handle files with a particular extension. Listing Six shows its definition and usage.

Listing Six

// DefinitionFileConverter* FileConverterStore::CreateByExtension(
                   const char* ext)
{
    for (unsigned i=0; i< m_pConverters->size(); i++)
    if (stricmp( m_pConverters->at(i)->GetExtension(),
                ext ) == 0)
        return m_pConverters->at(i)->CreateObject();
    return NULL;
 }

// Usage
void SomeFunc()
{
    FileConverter* pConverter;
    pConverter = gConverterStore.CreateByExtension(".TIF");
    if (pConverter)
        pConverter->Import("Image.TIF");
    delete pConverter;
}C++ LOCALES

The code in Listing Six works almost exactly like the CreateConversionObject() function, except now the list of conversion objects is built dynamically at run time.

To support shoppers who are "just browsing," the store has two member functions, GetCount() and GetAt(), which allow indexed retrieval of each of the available converters; these are defined in Store.cpp (available electronically). As shown in Main.cpp, these functions can be used to write a function that prints the extensions of all compressed formats.

Summary

The specialty store example shows how you can tightly encapsulate objects so that the existence of the class is hidden from other classes at compile time. Objects are created based on one or more criteria, instead of on a concrete name. This mechanism allows new classes to be registered at run time, even if they live in a shared library or DLL. Once registered, these new classes will be treated exactly the same as any built-in classes. (The sample code that's available electronically was written using Visual C++ 4.2. I've also tested it with Visual C++ 5.0. It does not require MFC, but does use STL.)

The specialty store is made up of the store itself, which is essentially an object registry, a set of related classes (such as the conversion objects used in this article), and a set of proxy objects, which are instantiations of template classes that go in the object registry and represent the classes that can be created. The proxy classes provide a common interface for publicizing the criteria that defines the objects.

Specialty stores are particularly useful in situations where the target environment is highly variable, such as programs that rely heavily on networking protocols. Any particular system may support many different protocols. With a specialty store, all of the protocols can be analyzed at run time and only those supported by the local configuration will be installed.


Jim is the author of Multithreading Applications in Win32 (Addison-Wesley, 1997) and a technical leader for Turning Point Software. He can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.