21

My goal is to have global constants in a C++ game I'm working on (to represent some graphics info and the like). My current implementation is to toss them all in a .h and include them everywhere. This works, except that every time I change a setting, the entire code base must be recompiled.

So, my next idea was to toss them in some configuration txt file and parse them in, that way no code is actually changed when settings change. The parser was simple enough, and I could put the values into the constants, but because the parser was a code block, the constants were no longer global.

Is there a good way to solve this? Perhaps some way to make them global despite being in a block or some way to avoid recompiling everything when changing settings?

Cannoliopsida
  • 3,044
  • 5
  • 36
  • 61

4 Answers4

27

The way I used solve this is to put the variables in a separate global namespace, which is in a header file named something like config.h, then include that file everywhere.

// In config.h

#ifndef CONFIG_H
#define CONFIG_H

namespace config
{
    extern int some_config_int;
    extern std::string some_config_string;

    bool load_config_file();
}

#endif

Then in a source file, you define the variable and also set them to a default value. This source file also have the code to load the variables from your configuration file.

// In config.cpp

namespace config
{
    int some_config_int = 123;
    std::string some_config_string = "foo";
}

bool config::load_config_file()
{
    // Code to load and set the configuration variables
}

Now in every source file you need the configuration variables, include config.h and access them like config::some_config_int.

However, there is no "proper" way of solving this, all ways that work are proper in my eyes.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • This solution sounds really ideal, because it lets me load in a config file, so a user could change settings without any compiling. But, I tried to exactly imitate the code logic you used, and I'm getting "undefined reference to 'config::VARIABLE_NAME' "" every time any variable is used. Even when I didn't use any load function and purely defined the variables in the .cpp, I got the same error. (I used the exact same syntax as you with extern in .h and full declaration and definition in .cpp) – Cannoliopsida Jun 05 '12 at 06:35
  • @Akroy You do build/link with the new source file? – Some programmer dude Jun 05 '12 at 06:37
  • I did. So, if config.h has namespace config{ extern int x;}, config.cpp has namespace config{ int x=5;}, then in another file that has #include "config.h", I should be able to access config::x? Is there something I'm missing? – Cannoliopsida Jun 05 '12 at 06:43
  • 1
    Huh, I've tried and tried, but nothing. If I have those lines alone in a files, it works, but as soon as I put in the rest of my code, I get all sorts of undefined reference errors. None of my code declares using namespace, so I have no idea what causes this. – Cannoliopsida Jun 05 '12 at 07:23
21

Another way to do this would be to create a singleton class.

#include <fstream>
#include <map>
#include <string>

class ConfigStore
{
public:
    static ConfigStore& get()
    {
        static ConfigStore instance;
        return instance;
    }
    void parseFile(std::ifstream& inStream);
    template<typename _T>
    _T getValue(std::string key);
private:
    ConfigStore(){};
    ConfigStore(const ConfigStore&);
    ConfigStore& operator=(const ConfigStore&);
    std::map<std::string,std::string> storedConfig;
};

Here the configuration is saved in a map, meaning as long as parseFile can read the file and getValue can parse the type there is no need to recompile the config class if you add new keys.

Usage:

std::ifstream input("somefile.txt");
ConfigStore::get().parseFile(input);
std::cout<<ConfigStore::get().getValue<std::string>(std::string("thing"))<<std::endl;
denahiro
  • 1,211
  • 7
  • 10
  • Admittedly I originally wanted to do it without added constructs like this, but this follows more OO conventions and is working better in general! I liked Joachim's suggestions as well, but this one is what solved it for me, thank you! – Cannoliopsida Jun 06 '12 at 04:30
  • 1
    @akroy I beg to disagree. What kind of OO conventions do you even mean here? We have a singleton. Some programmer dude's solution seems clearly superior to introducing a singleton that is basically built around a `std::map` with slow lookup and without type safety with no real benefit over bare variables – IceFire Apr 17 '17 at 16:26
  • Hey IceFire! For context, this question is from 5 years ago when I was first learning to program. Looking at the comment thread, it looks like I also preferred Some programmer dude's solution in theory, but couldn't get it past the compiler. So, if it compiles for you, more power to you. :) – Cannoliopsida Apr 17 '17 at 19:22
  • No worries and this is by no means personal anyways! If Google drops a thread at a common search term, though, I always find it useful to clarify things for the new readers who might become confused otherwise – IceFire Apr 17 '17 at 20:08
2

What about creating functions that return your constants that you can specify in a .cxx file? For example:

// foo.h
const int BAR();

// foo.cxx
const int BAR() {
    return 10;
};
Stephan
  • 1,007
  • 7
  • 9
1

put only the declarations in head file and put the definitions in a cpp file. then you change the definitions in cpp file will not cause all code recompiled

RolandXu
  • 3,566
  • 2
  • 17
  • 23