Reputation: 1281
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
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