Search code examples
c++boostboost-program-options

Parsing a configuration file with boost::program_options


Good day,

i wrote a class to parse a configuration file via boost::program_options. Here is what I have (shortened):

namespace nsProOp = boost::program_options;
nsProOp::variables_map m_variableMap;
nsProOp::options_description m_description;



// To add options to the variableMap, e.g. "addOption<int>("money_amount");"
template <class T>
    void addOption(const std::string& option, const std::string& helpDescription = "") {
        m_description.add_options()(option.c_str(), nsProOp::value<T > (), helpDescription.c_str());
    }



// And this is how i actually read the file:
void ConfigFile::parse() {
    std::ifstream file;
    file.open(m_pathToFile.c_str());

    nsProOp::store(nsProOp::parse_config_file(file, m_description, true), m_variableMap);
    nsProOp::notify(m_variableMap);      
}

Okay, this works fine. But i want to be able to parse the same file again so that I always use the latest entries provided by the user! The boost documentation says about "store":

"Stores in 'm' all options that are defined in 'options'. If 'm' already has a non-defaulted value of an option, that value is not changed, even if 'options' specify some value."

So, if I call "parse()" again nothing happens, because m_variableMap is filled. My attempt to call m_variableMap.clear() does not solve my problem, so store only works the first time.

Has anybody an advice for me? If my question is unclear, just tell me. Thanks!


Solution

  • In at least boost 1.50, variables_map::clear() will allow the variable map to be properly refilled via store. An alternative solution that works as far back as at least boost 1.37 is to assign a default constructed variable map into the variable map before calling store.

    void ConfigFile::parse() {
      std::ifstream file;
      file.open(m_pathToFile.c_str());
      m_variableMap = nsProOp::variables_map(); // Clear m_variableMap.
      nsProOp::store(nsProOp::parse_config_file(file, m_description, true), 
                     m_variableMap);
      nsProOp::notify(m_variableMap);      
    }
    

    Here is a sample program:

    #include <boost/program_options.hpp>
    #include <iostream>
    #include <fstream>
    #include <string>
    
    namespace po = boost::program_options;
    
    void write_settings(const char* value)
    {
      std::ofstream settings_file("settings.ini");
      settings_file << "name = " << value;
    }
    
    void read_settings(po::options_description& desc,
                       po::variables_map& vm)
    {
      std::ifstream settings_file("settings.ini");
    
      // Clear the map.
      vm = po::variables_map();
    
      po::store(po::parse_config_file(settings_file , desc), vm);
      po::notify(vm);    
    }
    
    int main()
    {
      std::string name;
    
      // Setup options.
      po::options_description desc("Options");
      desc.add_options()
        ("name", po::value<std::string>(&name), "name");
      po::variables_map vm;
    
      // Write, read, and print settings.
      write_settings("test");
      read_settings(desc, vm);
      std::cout << "name = " << name << std::endl;
    
      // Write, read, and print newer settings.
      write_settings("another test");
      read_settings(desc, vm);
      std::cout << "name = " << name << std::endl;
    }
    

    Which produces the following output:

    name = test
    name = another test