Rolle
Rolle

Reputation: 2980

What's the proper "C++ way" to do global variables?

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?

Upvotes: 14

Views: 8521

Answers (11)

Larry Watanabe
Larry Watanabe

Reputation: 10184

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 ) {
     }
     ....
}

Upvotes: 0

Partial
Partial

Reputation: 9989

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

Upvotes: 0

Brian R. Bondy
Brian R. Bondy

Reputation: 347216

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.

Upvotes: 12

Roger Nelson
Roger Nelson

Reputation: 1912

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).

Upvotes: 2

markh44
markh44

Reputation: 6080

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.

Upvotes: 4

Dima
Dima

Reputation: 4128

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.

Upvotes: 0

Jasper Bekkers
Jasper Bekkers

Reputation: 6809

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;

Upvotes: 6

Loki Astari
Loki Astari

Reputation: 264381

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.

Upvotes: -1

John MacIntyre
John MacIntyre

Reputation: 13021

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.

Upvotes: 0

Mykola Golubyev
Mykola Golubyev

Reputation: 59824

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.

Upvotes: 8

Anton Gogolev
Anton Gogolev

Reputation: 115731

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.

Upvotes: -1

Related Questions