13

I have a main application class, which contains a logger, plus some general app configurations, etc.

Now I will display a lot of GUI windows and so on (that will use the logger and configs), and I don't want to pass the logger and configurations to every single constructor.

I have seen some variants, like declaring the main class extern everywhere, but that doesn't feel very object oriented. What is the "standard" C++ way to make elements in the main class accessible to all (or most) other classes?

Brian R. Bondy
  • 314,085
  • 114
  • 576
  • 619
Rolle
  • 2,547
  • 5
  • 31
  • 37
  • 4
    An interesting discussion on how to implement a singleton, along with thread-safety in C++ can be found in this paper: http://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf –  Oct 30 '09 at 11:37

11 Answers11

13

Use the singleton design pattern.

Basically you return a static instance of an object and use that for all of your work.

Please see this link about how to use a singleton and also this stackoverflow link about when you should not use it

Warning: The singleton pattern involves promoting global state. Global state is bad for many reasons.
For example: unit testing.

Community
  • 1
  • 1
Brian R. Bondy
  • 314,085
  • 114
  • 576
  • 619
  • 1
    But be aware! Use Your Singletons Wisely: http://www.ibm.com/developerworks/webservices/library/co-single.html – toxvaerd Apr 21 '09 at 14:06
  • See also: http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – Reunanen Apr 21 '09 at 14:13
  • 1
    A logger is one of the few places that a singleton is a good choice! – Martin Beckett Apr 21 '09 at 15:06
  • 1
    No. Why did this get accepted? Singletons and globals are completely different things. If you need something to be global, make it global. But making something global does not mean that it should also be limited to one instance. By the way, a logger is not a good place for a singleton *either*. I've thought so before, and then a few months later, cursed as I had to add a second logger after all. It saves so much work to just not make it a singleton in the first place. – jalf Apr 21 '09 at 16:24
  • It's not a global variable, but it gets around having to use a global with a bunch of externs. I wasn't suggesting for him to change his code to have only 1 logger in the first place, that's already present in his code. But it is a more object oriented way then he is currently using. As of right now he has a single one anyway. I also provided the 2 warning links about when not to use it. – Brian R. Bondy Apr 21 '09 at 16:39
  • Good answer. Using DI is the next step--but got to get used to letting people deal with singletons first :) – Bill K Jun 10 '09 at 23:29
  • @jalf: I think many people consider the singleton pattern to be 2 things: 1) restricting to only 1 of something 2) to have global state. Maybe not all interpretations of the singleton pattern are the same though. – Brian R. Bondy Jun 10 '09 at 23:30
  • @jalf Just out of curiosity, if something is global, how is it not limited to one instance? How do you reference a second one? Producing a second one would shadow the first unless it had a different name in which case you have 2 global names which have to be produced and stored somewhere (like a singleton). – Bill K Jun 11 '09 at 16:33
  • A singleton is not a global variable in any way. In fact it is usually a reference or pointer of a static instance of an object that can not be copied in any way. NEVER USE GLOBAL VARIABLES! – Partial Jun 24 '09 at 19:02
  • Pretty sure singletons are implemented using a static variable. Which gives global state of that object. – Brian R. Bondy Jun 24 '09 at 19:17
  • @Brian R. Bondy: Global variables and the "global state" of a static member variable are two different things. Unlike a global variable, you can control the behavior of that static instance. A singleton does exactly that... allow clients of the singleton class a certain instance that cannot be created by them nor be copied. With a global variable anyone can change it at anytime and it can lead to evil stuff! :P – Partial Jun 25 '09 at 06:17
  • @Partial: I did not claim that global variables and global state was the same thing. My claim was that the singleton pattern involves global state. – Brian R. Bondy Jun 25 '09 at 11:27
8

It is not so bad idea to pass the logger and config to all the constructors if your logger and config is abstract enough.

Singleton can be a problem in the future. But it seams like a right choice in the project begin. Your choice. If your project is small enough - go with singleton. If not - dependency injection.

Mykola Golubyev
  • 52,197
  • 14
  • 81
  • 101
  • If you are just appending to a file may not be so bad, but if you have to initialize a logging framework, then you might not want to reinitialize it every time. – John MacIntyre Apr 21 '09 at 14:06
  • 1
    reinitialize? Why? You create once in the top and pass it to the bottom to all services that needs it. – Mykola Golubyev Apr 21 '09 at 14:08
  • Sorry, I misread "It is not so bad idea to pass the logger and config to all the constructors..." – John MacIntyre Apr 21 '09 at 14:10
  • Or just collect what you need to pass around and put them all into a class/struct and pass around everything you need in one parameter. – Duck Apr 21 '09 at 15:38
  • +1 for dependency injection, the esp useful for unit testing. I even use it with a singletons to avoid the Singleton::instance() calls every where, IMHO clients shouldn't care if the object is a singleton. – iain Apr 21 '09 at 16:33
6

Why not use the system that's already in place? That is, redirect std::clog to output to a file and write to std::clog.

std::fstream *f = new std::fstream("./my_logfile.log")

std::clog.rdbuf(f->rdbuf());

std::clog << "Line of log information" << std::endl;
Jasper Bekkers
  • 6,495
  • 30
  • 45
  • you lose the consistent formatting (defined in one place) of a real logger, and you potentially also lose utility functions for outputting certain non-standard info (i.e. no string conversion). but for a small project, this is a really quick and easy way to go. – rmeador Apr 21 '09 at 15:31
  • 1
    Even if you don't want to use std::clog, you can still learn from it. clog is a global variable. If you want your own logger, make it a global variable. – jalf Apr 21 '09 at 16:25
  • @jalf: It actually uses the "Construct On First Use"-Idiom to create an instance only when needed. – Jasper Bekkers Apr 21 '09 at 18:38
  • 1
    @rmeador: The formatting consistency can be solved with custom stream modifiers (eg. clog << warning() << "log") which would print a warning, with the correct timestamp etc. I don't see how there is anything that couldn't be printed; apart from 3rd party stuff that you don't have any control over. – Jasper Bekkers Apr 21 '09 at 18:44
4

I'd agree with some kind of singleton approach. You definitely don't want to pass logger objects around all over the place. That will get very boring very quickly, and IMHO is a worse design than just having a plain global object.

A good test of whether you've got a good solution is the steps required to get the logging working in a function that needs it.

If you have to do much more than

#include "Logger.h"
...
void SomeFunction()
{
    ...
    LOGERROR << "SomeFunction is broken";   
    ...
}
...

then you are wasting effort.

markh44
  • 5,344
  • 5
  • 25
  • 33
2

Logging falls under the realm of 'separation of concern' as in aspect orient programming

Generally logging is not a function or concern of an object (for example, it does not change the state of the object; it is merely a mechanism for observing/recording the state, and the output is essentially disposable in most contexts) It is an ephemeral and often optional side function that does not contribute to the operation of a class. An object's method may perform logging, but the logging may be done there because it is a convenient place to do it or that point in the code execution stream is where one desires the state to be recorded.

Because C++ does not provide facilities for defining aspects, I tend to simply keep essentially external ephemeral objects like loggers global and wrap them in a namespace to sort of contain them. Namespaces are not intended for containment so this is kind of ugly, but for for lack of anything else it is convenient and is far less ugly and inconvienent than passing loggers in formal parameters or referencing them in all the objects you want to log. This also makes it easier to remove the logger if at some point I decide I no longer need the logger (I.e. if it was only used for debugging).

Roger Nelson
  • 1,824
  • 2
  • 14
  • 21
  • likewise, having it global in a namespace allows for an easier implementation of a locking mechnisim for multi-threaded apps. – Ape-inago Jun 11 '09 at 01:46
0

Why has no one thought of heritage and polymorphism? You could also use an abstract factory with that singleton ;)

Partial
  • 8,263
  • 12
  • 39
  • 57
0

Don't know if this is helpful in your situation or not, but in MFC, there was/is an application class.

I use to throw things like this into that class.

I assume you are not using MFC, but if you have an application class or something similar, this might be helpful.

John MacIntyre
  • 12,653
  • 12
  • 62
  • 102
0

Why not use log4cxx? Such problems are solved long ago and widely used by many. Unless you're building some very special logging system of your own... In such case, I'd use Factory pattern which would create loggers for anyone interested (or giving away existing instance if it's singleton). Other classes would use factory to obtain the logger. Passing loggers in constructor parameters is a bad idea, because it couples your class with logger.

Dima
  • 3,859
  • 4
  • 36
  • 44
0

Simply pass your main class into the constructor of the other classes that you want to have access to "everything"

Then you can provide access to the logger etc. via member properties. (Forgive my C++ syntax, this is just a made-up language called "C++ confused by VB")

e.g.

Class App {
     Private  m_logger;
     Private  m_config;

     Public logger() {
        return m_logger;
     }

     Public config() {
        return m_config
     }
}

Class Window1 {
     New( anApp ) {
     }
     ....
}
Jason Plank
  • 2,322
  • 4
  • 29
  • 39
Larry Watanabe
  • 9,690
  • 9
  • 39
  • 45
-1

I guess Service Locator will do. That you'll have to either pass around in constructors, or have a globally accessible static member function in some well-known location. The former option is much more preferable.

Anton Gogolev
  • 107,051
  • 37
  • 191
  • 278
-1

I would avoid the singleton pattern.
Too many problems when it comes to testing and all that (see What is so bad about singletons?)

Personally I would pass the logger etc into the constructor. Alternatively you can use a factory to create/pass a reference to the resource.

Community
  • 1
  • 1
Martin York
  • 234,851
  • 74
  • 306
  • 532
  • Properly executed, there's really no difference between using a singleton available anywhere you use its #include, and your alternatives, except with your alternative you have to pass a reference at every level. – kmarsh Jun 24 '09 at 19:21