Saturday, March 3, 2012

Configuration file parser - C++

I'm working on a project developed in C++, which can be configured using several parameters on runtime. Since there were lots of options, i decided to include a configuration file in which the user could assign a value to each defined attribute. This is an example of the file's structure:

 


# Default yasps configuration
# By default the server is bound to 0.0.0.0:7517
# --------------------------------------------------
# Server configuration
bind_address=0.0.0.0
port=7517

# Authentication configuration
# Unauthenticated connections are allowed by default
noauth=1
username=user
password=pass123

As you can see, the options require different data types. Therefore, i needed a generic algorithm that could parse the file and interpret the given values as strings, integer, bools or whatever data type i indicated, and assign them to the corresponding attribute. To achieve this, i created a small class using template parameters.

The ConfigurationParser is extremely simple to use. There is one method which adds an attribute and associates it with a pointer. Whenever that attribute name is found on the file, the parser will try to interpret the given value and store it in that pointer. The method has the following signature:

template<class T>

void add_option(const std::string &name, T *value_ptr);  

The only constraint imposed on the type T is that the input operator(operator>>) is defined. As long as you use either primitive types or std::string(s), you don't have to implement anything else. In case you have created a certain class that can be deserialized, you would have to implement this operator.

Once every attribute has been set, you have to call the ConfigurationParser::parse method, using the configuration file name as the argument. This is the signature of this method:

void parse(const std::string &file_name);

This method can raise different exceptions, depending on what problem was encountered:
  • std::ios_base::failure if an error occurred when opening the file.
  • ConfigurationParser::NoValueGivenError if no value was set for an attribute that appeared on the configuration file. e.g. bleh= . There should be some value after the '=' character.
  • ConfigurationParser::InvalidValueError if there was a data type missmatch when trying to interpret an attribute's value. This can happen if, for example, an attribute expects an integer value, however, a string value is given.
  • ConfigurationParser::InvalidOptionError is raised if an attribute which was not registered using ConfigurationParser::add_option appeared in the configuration file.
No that the ConfigurationParser class is included inside the CPPUtils namespace. Finally, here is an example, taken from the project i'm working on:


#include <iostream>
#include <string>
#include "configparser.h" 

using CPPUtils::ConfigurationParser;

class Configuration {
public:
    void load(const std::string &file_name) {
        ConfigurationParser parser;
        parser.add_option("username", &config_username);
        parser.add_option("password", &config_password);
        parser.add_option("bind_address", &address);
        parser.add_option("log_file", &log_filename);
        parser.add_option("port", &port);
        parser.add_option("noauth", &config_allow_no_auth);
        parser.add_option("enable_logging", &config_enable_logging);
        try {
            parser.parse(file_name);
        }
        catch(std::ios_base::failure &ex) {
            std::cerr << "[ERROR] Error opening " << file_name << "(" << ex.what() << ").\n";
        }
        catch(ConfigurationParser::NoValueGivenError &ex) {
            std::cerr << "[ERROR] Parse error: No value give for " << ex.what() << " attribute.\n";
        }
        catch(ConfigurationParser::InvalidValueError &ex) {
            std::cerr << "[ERROR] Parse error: Invalid value for attribute " << ex.what() << "\n";
        }
        catch(ConfigurationParser::InvalidOptionError &ex) {
            std::cerr << "[ERROR] Parse error: Could not find a valid attribute in \"" << ex.what() << "\"\n";;
        }
    }
private:
    std::string config_username, config_password, address, log_filename;
    short port;
    bool config_allow_no_auth, config_enable_logging;
}; 


That's all. You can download the header file here, the source file here and the only header dependency, exception.h. The class is licensed under GPLv3, so feel free to use it.

No comments:

Post a Comment