Boost logo

Boost :

Subject: Re: [boost] Is there interest in an alternative to the Singleton anti-pattern?
From: Ben Robinson (icaretaker_at_[hidden])
Date: 2011-06-25 02:51:24


>
> > I look forward to your feedback on the Singularity Pattern. The
> unittests
> > in Vault/Singularity/singularity_unittest.cpp are great to study first to
> > see how Singularity is meant to be used.
>
> I have some remarks:
>
> 1) class single_threaded - if you remove user defined destructor and
> constructor, it will become a POD type and compillers will do better
> optimizations
>
> I completely agree. Done. Thank you.

> 2) singularity_state, singularity_instance and singularity classes can
> be combined together if you remove
> BOOST_PP_REPEAT(BOOST_SINGULARITY_ARGS_MAX,
> BOOST_PP_DEF_CLASS_TYPE_DEFAULT, _) from singularity and put this
> templates directly for create functions. If you do so, code like:
>
> singularity<Horizon, single_threaded, int, Event*, Event&>::create(3,
> &event, event);
>
> will look much better, just like:
>
> singularity<Horizon, single_threaded>::create(3, &event, event);
>
> Then, FRIEND_CLASS_SINGULARITY macro will be much shorter
>
>
You suggestion simplifies the interface by implicitly determing the types
for the user, rather than requiring them to manually specify them. But in
addition, by only generating create(...) overloads that pass by reference or
pointer (not by value which the user could have done before), we prevent the
undesirable situation where a user would pass an object by value into
Singularity which would then pass by value into their constructor. Now, all
arguments which are passed by value are accepted by their reference, and
then passed either by value, or by reference, into the constructor,
whichever is present. A class obviously cannot provide both a value, and a
reference constructor for the same type, so there are no ambiguity problems.
 An excellent suggestion.

One detail, I still require the singularity_state and singularity_instance
structs, because state should not depend on the threading model, and
instance should not depend on the access model (see point 5 below).

The only downside is that I must generate 2^(n+1)-1 function overloads if I
am to support constructors up to a maximum of n arguments. I am basically
permuting receiving the typed arguments by reference or by address. I have
implemented Singularity without the BOOST_PP meta generating functions for
now up to three arguments. Once the design is finalized, I will once again
generate the create() functions based on BOOST_SINGULARITY_ARGS_MAX.

4) May be, it would be much better to derive from singularity. Then
> the code will look just like this:
> Horizon::create(3, &event, event);
> or like:
> Horizon< single_threaded >::create(3, &event, event);
>

I did not need to implement this suggestion, as implementing #2 also allowed
this usage pattern. As such, Singularity is now usable as both a factory,
or as a base class. I have unit tests and code comments demonstrating both
usage patterns. This makes Singularity now far more usable, and general
purpose.

> 5) It would be great, to have a get() function in singularity, that
> returns reference
>
> One of the selling points of Singularity is that it did not provide global
access to an instance. However, I can see situations where this would be
desirable, and also providing global access will make transitioning from
Singleton much easier. Therefore, I have created an additional policy
argument, which has a default value of "local_access", causing get() to
throw an exception. However, if you instantiate Singularity with
"global_access", you can now access the instance, provided create() has
already been called and destroy() has not.

> It's just what I saw in a 15 minutes of review. I will make a more
> deep look a little bit later. Any comments are welcomed.
>
>
> Best regards.
> Antony Polukhin
>

Keep the feedback coming. I was going to post updated source and both
AsFactory and AsBaseClass unit tests, to the Vault, but apparently the Vault
is now deprecated. You can email me at icaretaker_at_[hidden] if you would
like to see the new code. Here is an example of the new usage pattern:

// Usage as a Factory:
typedef singularity<Horizon, single_threaded, local_access>
HorizonSingularityType; // same as singularity<Horizon>
Horizon & horizonA = HorizonSingularityType::create(3, &event, event);
HorizonSingularityType::destroy();

// Usage as a Base Class:
class Horizon : public singularity<Horizon, multi_threaded, global_access>
Horizon & horizonB = Horizon::create(3, &event, event);
Horizon::destroy();

Thank you,

Ben Robinson, Ph.D.


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