Boost logo

Boost :

From: Dave Handley (dave_at_[hidden])
Date: 2005-01-07 15:39:11


Jason Hise wrote:

>
> I realize now that there is little interest in considering my library
> for Boost over an adaptation of the Loki library... however, even if my
> implementation is not under consideration, would someone be willing to
> explain what advantages Loki offers over it?
>

The key thing that Loki does is use modern C++ design methods to allow a
policy driven choice of a number of design questions. These are:

1) The Creation Policy - this can mean creation from new, malloc, or
create statically.
2) The Lifetime Policy - this can mean that you use a Phoenix Singleton,
Longevity managed singleton, or just using normal C++ rules.
3) The threading policy - single or multi-threaded.

The key disadvantage of your implementation (in my view) is that it forces
the use of a single creation policy (create statically), a single lifetime
policy (dependencies), and a single threading policy (single-threaded).
Whilst it would be relatively easy to change any of these, it would involve
re-writing the class, which moves away from the concept of generic,
re-usable code.

> Dave Handley wrote:
>
>> 1) Wouldn't it be better to use CRTP than the rather strange usage
>> syntax that Loki uses with a typedef and a requirement to make the
>> constructors/destructors private? It is remarkably easy if you are
>> not familiar with the Singleton implementation in Loki to make your
>> constructors/destructors public by mistake and then use them, whereas
>> a CRTP solution could make the base constructors/destructors protected.
>
> With my implementation it is perfectly safe for client code to make
> public constructors and destructors and not have to worry about the
> class being used the wrong way, because the inheritance method I used
> makes it pure virtual.
>

I agree - and that is why I have stated the usage syntax in Loki is
something I don't like - I would much prefer to see the curiously recurring
template pattern (CRTP) being used. For your info, if you are not familiar
with CRTP then you would have a new singleton called MySingleton that looked
something like:

class MySingleton : public generic_singleton<MySingleton> etc.

The generic_singleton class can then enforce protected
constructors/destructors and private copy constructors/equals operators.
This has a few advantages over the model used in your code. The main thing
is that it is a little more readable.

Your version:
Singleton<MySingleton>::GetInst()
gets the singleton pointer
whereas with CRTP:
MySingleton::GetInst()
would get the singleton pointer.

>>
>> 2) There are probably some more options that need to be included,
>> for example making a Singleton be capable of destroying/recreating
>> during the run of a program. Consider a singleton that is managing a
>> print queue. For some reason all the printers go off-line, there is a
>> potential for memory optimisation by closing down the print queue. If
>> printers come back on line the print queue could come back on line -
>> but that isn't possible (as far as I know) with Loki.
>>
> With my revised usage of dependencies, the singleton would be destroyed
> when there were no dependencies left and recreated when new ones were
> added. If client code wants to ensure that the singleton's life is not
> interrupted, they could add a dependency in main, or even add a
> static/global dependency to ensure that the lifetime extends beyond main
> if that feature is desired. Using dependencies also can ensure that
> singletons which rely on each other are always created and destroyed in
> the right order.

Dependencies are certainly one way of managing the lifetime of a singleton,
but in a large project you would need to be aware of some potential flaws
which your implementation would not pick up. Specifically, if you
accidentally created a cycle of dependencies, the Singletons will never be
destroyed. This is an easy thing to pick up in a small project, but in a
large project maintained with lots of programmers over a number of years, I
can guarantee that this will happen at some point. This is one of the
reasons for a longevity based system in Loki - it doesn't matter if you get
2 singletons with the same longevity since they will just be deleted in
reverse order of creation.

>
> Again, I am unfamiliar with Loki, so I am sure that there are
> considerations that it handles of which I am unaware. I would just like
> to learn what these considerations are so that I might grow as a
> programmer, and learn through improving my own implementation. Thanks
> in advance.
>

A final point which I'm pretty sure someone mentioned on a previous post.
Your implementation includes a virtual function in the SingletonBase. A
singleton is a simple object that doesn't necessarily need a v-table. By
having a virtual function in the base, you can never create a singleton that
doesn't have a v-table. Also as an aside, whilst both SingletonBase and
Singleton contain virtual functions, they do not declare the destructor
virtual but probably should! Using CRTP, you should get rid of the need for
any virtual functions. Remember that Singletons should be pretty simple -
look at the code in the GoF book and you see what I mean:

// Declaration
class Singleton {
public:
    static Singleton* Instance();
protected:
    Singleton();
private:
    static Singleton* _instance;
}

Your solution isn't wrong, its just that Loki does a whole lot more that
makes it a better library. The point I am making with my posts is that if
Boost is going to create a library with a singleton in it, it should, as a
minimum, do everything that Loki does. It should also try to fix some of
the problems with Loki as well along the way...

Dave


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