Re: StateFull vs Stateless Singleton
On Jul 5, 6:05 pm, =D6=F6 Tiib <oot...@hot.ee> wrote:
On 5 juuli, 17:38, James Kanze <james.ka...@gmail.com> wrote:
On Jul 5, 12:31 am, =D6=F6 Tiib <oot...@hot.ee> wrote:
On 4 juuli, 23:58, sebastian <sebastianga...@gmail.com> wrote:
[...]
Looks as beautiful like any other reinvented square wheel.
The requirements seem to be that such code must work:
int main( void )
{
{
foo a;
foo::instance( ).bar( );
}
foo c; // Second singleton and *NO* Whoops
foo::instance( ).bar( );
}
Not sure why someone needs to have global state? Typical
example of singletons is a logger. Huh? So ... OK. What if
i need to have separate logs from different modules?
I will then need new class for each log to have separate
singleton logger for each module? Nonsense.
So your log manager is a singleton. Somewhere, there must
be a single bit of code which reads the configuration file
for the log.
I am not saying there may not be single instances of
something. I am saying that these should not be in some global
state,
It's not "global" state:-).
so there is danger to produce multiple by mistake or
copy it and so it has to be prevented by singleton pattern.
Singular state is perfectly normal. Where it is needed hide
it into implementation to keep interface laconic. That log
configuration should be internal property of logging framework
and not some global self-owned state for anyone to ask
instance() for. Not even as private static member. Translation
unit local is perfect. Then it is function debugging::log()
that consults with private log manager (if there exist such)
to find out where it logs and how (if that differs) and not
everyone typing nonsense like:
debugging::LogManager::instance().produceLogger().doYourTrick("bla
bla");
Well, logging is a very special case. Independantly of the
singleton argument, you need to keep the invocations as succinct
as possible, and the test whether the given level is active or
not as fast as possible. This suggests macros (which also allow
automatic insertion of __LINE__ and __FILE__), and some sort of
static variables, rather than a test in an instance functions.
I'll usually use something like:
extern LogStream* logOutput[];
#define LOG(n) logOutput[n] != NULL
&& *logOutput[n] << __FILE__ << ":" << __LINE__
And of course, LogStream can't be a singleton. But the code
which initializes logOutput might be, especially if it has to
wait for external events and possibly update the logOutput
table. And in very large applications, there's likely to be
a logOutput[] instance per subsystem, wrapped in a class, with
a constructor which registers it with a central log manager,
which reads the log configuration when instructed to (since you
can't necessarily shut down the application just to change the
configuration of its log). In such cases, log manager is likely
to be very much like a classic singleton (if only to handle
order of initialization issues).
Similarly, the configuration manager is a singleton.
Depends what it manages. If "configuration" of dynamically
loadable modules then such thing must be there, but i would
call it "application" object (or its "configuration" member).
If these are module settings then these belong to each module.
It is sad that C++ does not contain concept of dynamically
loadable modules.
As is the code which captures the command line and its options.
Command line is passed to main() and so main() passes it to
some singleton? Why it should be a singleton?
Because there can't be more than one command line. Options have
to register before main is called (from the constructor of
a static object), so you need something to handle the order of
initialization issues.
These are command line arguments for something that will be
further called by main(). Why there must be a global singleton
that holds its options?
Because no one component is interested in all of the options.
However that does not worry me, after all there are very few
things that directly depend on command line and that command
line does not likely change during program run.
Not once it has been parsed. But Option enroles with the
CommandLine class, so that it can correctly pick up options when
it parses the command line.
In a multithreaded application, the thread manager (which
ensures e.g. a clean shutdown) must be a singleton.
At the moment yes, application (or some framework that it
uses) has to orchestrate shutdown itself. Again, when i do not
get rid of single things then i do not like
"ShutDownManager::instance().doIt()". I prefer to have
shutDown() function. If nothing else then faking it for a unit
test is lot cheaper.
The main argument against this solution is that it doesn't work.
The task manager has to know about all of the tasks, so that it
can signal them in case of a shutdown request, then wait until
they've finished.
(Thinking about it, this seems to be a common theme of
singletons. The have to know about all of the instances of
something else: threads, temporary files, etc.)
What if i need to have separate logs from different
threads? Then that Logger::instance() should give me
"different" singletons based on thread ID? Huh? Why it
can't just be debugging::log() and where it logs is its
internal business, no global state no nothing?
Huh? What is debugging::log()? (The syntax suggests
a static class member. A class instance function, in sum.
So that its data is a "singleton".)
No, i meant a namespace, sorry for not being clear there.
debugging::log() like std::sort(). Actually i suggest to wrap
logging into macro anyway. Some useful information like
file/function/ linenumber are impossible to gather in
a generic way without macros involved.
Yes. More generally, however, when single instances of data are
involved, I prefer to wrap in a class than in a namespace. Once
I've defined the class, no one else can get at the data.
--
James Kanze