Boost logo

Boost :

From: Anthony Williams (anthony_w.geo_at_[hidden])
Date: 2008-01-17 08:43:57


Tobias Schwinger <tschwinger <at> isonews2.com> writes:

>
> Anthony Williams wrote:
> > Tobias Schwinger <tschwinger <at> isonews2.com> writes:
> >> Anthony Williams wrote:
> >> Having a framework internally use some Singletons can greatly simplify
> >> its use.
> >
> > Possibly, but I don't think they're needed even then. If they're an
> > implementation detail of the framework, you don't need to make things a
> > singleton in order to ensure there is only one instance --- just create one.
>
> What's so different between "using a Singleton" and "just creating one"?

A singleton enforces that there is only one global instance with global
accessibility. If you just create one (and pass it down by dependency injection)
then the decision to just create one is made at the top level (where it
belongs), and the client code doesn't have to be fussed with how many instances
there are --- just that it can use the one provided.

void some_function(Logger& logger)
{
   logger.log("something");
}

int main()
{
  Logger myLogger; // ooh look, just one
  some_function(myLogger);
}

vs

void some_function()
{
  singleton<Logger>::instance()->log("something");
}

int main()
{
  some_function();
}
 
> In fact, "just creating one" should be exactly what this library is all
> about .

Singleton is also called the Highlander pattern --- there can be only one.

> >
> >> Exposing a singleton to a user provides more flexibility than
> >> exposing a static interface (and can also improve performance).
> >
> > I don't see how. You can easily rewrite a static interface to use a
> > singleton
> > internally. Having a framework provide the user with a (reference-counted)
> > pointer to an interface suitably hides all the details.
>
> Yes, rewriting the code that uses the interface to use a non-static one
> seems less trivial of a task, however.

shared_ptr<Logger> getLogger();

void some_function()
{
  getLogger()->log("something");
}

Client code doesn't have to know how many loggers there are. You could also pass
in a context object or factory:

void some_function(Context& context)
{
  context.getLogger()->log("something");
}

> >
> >> A "tendency towards overuse" is not a good reason to reject a library,
> >> as it won't stop overuse and encourages more half-baked solutions that
> >> are written in a hurry.
> >
> > It is better to educate people in better ways of doing things (and provide
> > tools to make those things easy) than enable them to easily do something
> > that's generally a bad idea.
>
> Without promoting Singletons people will use globals. And they will use
> more globals than they would use Singletons (especially in C++ because
> without we can't be sure initialization code is run if we're inside a
> dynamic library, so we probably end up using POD typed globals).

Dependency injection is my preferred means, rather than globals or singletons.
It has the side effect that you can decide on an initialization order that best
suits your uses.
 
> >>>> * What is your evaluation of the design?
> >>>> * What is your evaluation of the implementation?
> >>> The design mixes several independent issues --- ensuring there is only one
> >>> instance of a class and avoiding initialization order problems with
> >>> on-demand
> >>> initialization for starters.
> >>>
> >>> A simple wrapper class that allows for on-demand initialization, would be
> >>> useful. Conflating that with "there shall be at most one instance" is not.
> >>>
> >>> Again, allowing for a preferred destruction sequence of objects with such
> >>> on-demand initialization might also be useful, but does not belong with
> >>> the
> >>> one-instance constraint.
> >> What is the point in managing construction order without static context?
> >
> > Sometimes there are uses for globals. In those cases, it is important to
> > manage the construction and destruction order, independent of how many
> > instances of any given class there may be.
>
> OK. With a Singleton, the type serves as the key to access the global
> which I personally find quite elegant.

and which I find to be the biggest problem with singletons.
 
> A singleton can easily hold several instances of any other class, if a
> container is put into it.
>
> Further you can use multiple-inheritance to instantiate many singletons
> with a common subobject at compile time.
>
> So what are the alternatives (concrete interface proposals, please)?

One of the benefits you cite for your singleton design is the
construction-on-demand, which ensures that things are always constructed when
required. In that case, how about providing a class:

template<typename T>
class construct_on_demand;

so you can create instances like:

construct_on_demand<Logger> myLogger;

and any call to myLogger->log() will ensure that the object is constructed
before first use.

The appropriate operator-> call can also manage destruction order if required.

Alternatively, if you just pass in a factory or context object, as in my example
above, the getLogger() function can ensure the logger object is constructed
before use.
 
> >
> >> What is the point of having more than one instance of a class that lives
> >> in static context -- and how would it be captured syntacticly?
> >
> > I can imagine a use for having lots of instances of boost::mutex at global
> > scope --- each mutex protects a different thing. You don't need to capture
> > the
> > syntactic idea "there may be more than one of this class" --- that is the
> > base
> > level assumption.
> >
>
> Interestingly, you show us that in this very case the things you try to
> protect should be 'mutexed_singleton'S to avoid namespace pollution and
> to add clarity .

That was the first thought off the top of my head. It may be that a Locked<T>
template (such as that mentioned by Phil Endecott) would be a good idea, but
even then you've got multiple instances of the Locked<> template at global
scope. My point is that just because something is a global doesn't mean you only
want one instance of that *class*.

Anthony


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