Alexey104
Alexey104

Reputation: 1281

Are there any tricks I can use to initialize global constants with a separate function call?

I have a lot of global settings defining the behavior of my program. I found it convenient to declare these settings as extern constants in a separate namespace like this:

// settings.hpp

namespace settings
{
    extern const int waitEventTimeoutSeconds;
    // ...
}
// settings.cpp

#include "settings.hpp"

const int settings::waitEventTimeoutSeconds = 3;
// ...
// some.cpp

#include "settings.hpp"

bool waitForEvent(args...)
{
    const int timeout = settings::waitEventTimeoutSeconds;
    // Do stuff
}

It works fine, but in order to change some settings the user needs to edit settings.cpp file and recompile the stuff, which is not very convenient. I want some parseConfig() function that would be able to read a user provided config file once at startup and set global constants according to the content of that file:

void parseConfig(userConfigPath)
{
    settings::eventWaitTimeoutSettings = ...; // But it is const and global
}

Are there any tricks to achieve this?

Of course, I can just use some Config class for storing the settings, but this is why I don't like this approach:

Config userConfig(path);

bool bar(args..., timeout);

bool foo(args..., timeout)
{
    // Do stuff
    return bar(args..., timeout);
}

bool bar(args..., timeout)
{
    // Do stuff
    return waitForEvent(args..., timeout)
}

bool waitForEvent(args..., timeout)
{
    int waitFor = timeout;
    // Do stuff
}

int main()
{
    foo(args..., userConfig.waitEventTimeoutSeconds);
}

If using config object, a reference to this object or its members should be passed everywhere, which makes code more clunky. In the above example, when you look at foo() call in main() it is unclear why does this function need this weird userConfig.waitEventTimeoutSeconds parameter? Neither foo() nor bar() uses this parameter directly, so passing it just for further passing to a deeply nested function does not look like an elegant solution. This is why I prefer the global constants approach.

But can I somehow initialize all my global constants in a separate function?

Upvotes: 0

Views: 185

Answers (1)

KamilCuk
KamilCuk

Reputation: 141493

Do not use global variables. They make stuff confusing, like the problems you are having. Make it all local. Write getters and if needed setters. Force the user to call a function to get the configuration, where you do the stuff you want.

// settings.hpp
struct TheSettings {
   int waitEventTimeoutSeconds;
};

const TheSettings& settings();

// settings.cpp
const TheSettings& settings() {
    static TheSettings cachesettings;
    static bool cacheready = false;
    if (!cacheready) {      
        // open file
        // construct settings
        cachesettings.waitEventTimeoutSeconds = read_from_file;
        cacheready = true;
    }
    return cachesettings;
}


// main.cpp
int main() {
   // instead of `::` type `().`
   std::cout << settings().waitEventTimeoutSeconds;
}

You might be interested in reseraching C++ Singleton design pattern and Static Initialization Order Fiasco .

Upvotes: 1

Related Questions