Re: Singletons
On 12/11/2012 11:59 PM, ?? Tiib wrote:
On Tuesday, 11 December 2012 06:16:39 UTC+2, Balog Pal wrote:
Or the maitainance after the first effort. Ok, you finally get rid of
the app's global Logger singleton and instead of the few spots that must
emit log records just do so, half the system is passing a Logger
reference. Tomorrow you get a simple change request, that in part wants
a new place to log instead of an old one. again, instead of a few lines,
you have to build a passing track to the new place. And demolish to now
obsolete one to the old. Or chose to keep it in place, serving no useful
purpose -- make it tomorrow's maintainers problem to figure out why the
parasites are there.
You took quite weak example. Logger. Problems:
0) There is log in C++ for simple cases:
I mean that 'std::clog'. On simple cases just use it, why to reinvent a
wheel?
Come on now, std::clog is just a stupid stream, not a logger. Besides
tat I'm yet to see anyone use it in practice for anything.
While if it is usable, then how would it be a weak example -- unless you
immediately claim that we have an antipattern in the standard.
1) Reasons to use preprocessor:
Debug log is better if it contains bread crumbs pointing at source code
and line. Sometimes it is desirable for efficiency to entirely remove
debug logging (switch off evaluation of arguments) in production code.
I am talking about Log, you launch a side-track for some debug tracer.
Those are pretty different beasts.
Certainly Debug-only-Trace could also make an interesting example --
anyone here would like to add it to functions and classes?
2) Singleness is not certain:
I have worked with products where it was needed to log to different
targets. Per thread, per module, per user and/or per subject. Again some
security and/or efficiency reasons.
If design states there shall be a single one, then there is a single
one. I assure you that requirement do exist in many real systems. The
cases where multiples are needed are off-topik on this thread. Can we
keep the focus please?
3) Complex designs are not portable:
C++ just recently started to support threads, modules it does not
support, "user" has even no meaning on some platforms. So if portability
is needed despite of needs in 2) then it is something not doable in plain
C++.
I'm completely lost how that attaches to anything discussed. Btw C++
just recently put threads in the standard on platforms that support
threading C++ was usable to deal with threads decades earlier. And
portability between them was not so huge a problem either.
That all basically means that in my mind a Logger in C++ (with what there
is less need to refactor the code that calls it) is neither singleton
nor global object nor injected dependency. It is function-like MACRO.
Something usage of what looks like:
LOG_ERROR( format, subject % value % more_concerns );
or:
LOG( ERROR, problem, related_value, more_things );
And the macro gets expanded to something like GetTracer().AddEntry. Just
packing it in a macro hardly changes anything.
I guess you don't suggest it expand to something that uses a local
function parameter or class variable? And if so, all the possible
arguments listed for singletons apply for this approach.
What it really does? Short explanation: "Magic". Long explanation: it
depends on things conditionally included in platform.h, of what you
have fake version for unit-tests.
And the same magic can be used for a deal of other approaches. Yes,
macros are powerful, but also lead all style guides to avoid them as
much as possible for many good reasons.
Note that logging does not imply only huge, complex systems. Actually
log may be the only way to give valuable feedback (for tiny embedded
chip with its ring-buffers and ports).
Now that you mention embedded stuff, the last I did had macro-driven
trace, non-macro-driven log, and about a dozen subsystems, all being
singletons. I doubt anyone in sane mind would have suggested to pass
them around as params or excess state.
And the system had tests that executed the code on PC, without problem
to either just use or mock them -- or even emulate parts of the
underlying hardware (like the flash memory creating bit errors).
That in practice leads to more bad
design trying to pick up trends and emotionally suggested stuff instead
of actual thinking and understanding the alternatives.
Exactly. We have lot of alternatives how to enwrap or pass handle to
singular state. We have free functions, templates, lambdas, function
objects and stateless classes. We even have macros to fix language,
compiler and platform shortcomings. Replacing a "magic missile"
(singleton) with a "silver bullet" (dependency injection) is time badly
wasted if the reasons and the alternatives were left unconsidered.
Excuse me, but in the (abstract) design something is either stated as
singleton or not. If so, the implementation may use that information to
create a direct mapping, or may ignore it, and take a more general route.
In my practice the system normally has "library" parts and "application"
parts. The former is usually generic, and aims to be reused -- so takes
less assumptions. So here PFA is the norm and singletons are rare. In
the App's domain having singletons is okay and I can hardly recall a
single one with zero count.
I find those realms pretty different -- even when getting written by the
same programmer about in the same time -- and it leaves me baffled why
some people suggest to force or banish some methods in general, ignoring
at least that much context.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]