2013年12月28日 星期六

Implementations of singleton in C.

Singleton is usually an important and necessary pattern, no matter in any programming languages, for developing flows. The point which is cared here, is if the property could be assured consisted, could be checked by compiler.



The most common implement is based on the known pre-defined object and restricted method:
struct SingletonObject
{
    int Blahblah;
} *TheUniqueInstance = NULL;

struct SingletonObject *GetTheUniqueOne()
{
    if(!TheUniqueInstance)
    {
        TheUniqueInstance = (struct SingletonObject *)malloc(sizeof(struct Singleton));
        /* Initialise... */
    }

    return TheUniqueInstance;
}

Then Singleton is then achieved by restricting the access method. But at the same time it also builds the limitation that the property is embedded into the object. It becomes useless for the "virtual" singleton object, such as the situation a library need to make some object, which will be implemented in the application, be singleton; especially for the inherit action in C has less forcing ability. Furthermore, the access method below is not only thread-unsafe, but also expensive for performance critical programs. To generalize the singleton behavior, an alternative is registering to a container, called as register of singleton sometimes:

struct Container SingletonContainer;

int RegisterSingleton(void *ObjectNeedToBeSingleton)
{
    if(!ContainerFind(SingletonContainer, ObjectNeedToBeSingleton))
    { /* First applying. */
        ContainerInsert(SingletonContainer, ObjectNeedToBeSingleton);
        return 0;
    }
    else
    { /* Applied before. */
        return -1;
        /* Or using this to replace the existed one. */
    }
}

Instead of bundling the singleton property with object definition, registering to a container, say a list or a hash map, would give arbitrary object the property. And comparing to the function call in every access, the register process is only needed at the object constructing. Moreover, now the identifier for checking objects duplicated could be customized and defined in the `ContainerFind()' method, rather than the native type in the last way.
However, the thread-safety problem remains, and the registering process still could not be assured by the compiler: Every library user should be awared, understand, remember and follow the registering ceremony to gain the singleton property for their own object. To solve these problems, one another way is registering the object and its prototype to the global namespace, instead of our own container. Such like:

/* MySingletonObj.h */
extern struct Object MySingletonObj;

/* MySingletonObj.c */
struct Object
{
    int No;
    int *ptr;
};

static int DummyOneTwoThree = 123;
struct Object MySingletonObj = {456, &DummyOneTwoThree};

As in library situation, the library user can include `MySingletonObj.h' to access the singleton by its unique object name, but not create any other instance of type `struct Object'. The library user has no responsibility to do any thing to gain the singleton property, only to follow the pattern build the own object. And because of that, there is no function interface to construct the singleton object: it is done, and could only be done, by the initialization syntactic. It is useful when the singleton be the implementation of interfaces, usually tons of function pointers and constant settings. But it may not fit for object which has complex constructor gaining the singleton property.

沒有留言:

張貼留言