c++fileparsingtypes

How to parse config file with data of different types in C++?


Let's say we've got a config file with following data:

name=Ben
age=21
score=4.65

I want to parse this data and initialize custom class fields with it without defining datatype in process.

Custom class InpData:

class InpData {
  std::string name;
  int age;
  double score;
};

That is what I'd like to avoid:

InpData mydata;
while (std::getline(file, line) {
  std::istringstream iss(line);
  if (std::getline(iss, key, '=')) {
    if (key == "name")
       // read "Ben" to mydata.name
    else if (key == "age")
      // read "21" to mydata.age
    else if (key == "score")
      // read "4.65" to mydata.score
  }
}

In other words, I'd like to have something like std::map but with possibility to add data with different types.

I thought about std::variant or std::tuple, but as for me it's not what I need. I guess, some custom container is needed.


Solution

  • Sooner or later, you would need to specify the actual types because is a typed language.

    But if your problem is to avoid the branching based on the type (hence the reason why you want to use an std::map), what you can do is to read the data as std::strings and defer the proper conversion to another function (ideally via a contructor of InpData, but you can also do it via a "free function" which returns an InpData if you prefer).

    For example, you could define your data structure like:

    // Data structure
    struct InpData
    {
        std::string name;
        unsigned int age;
        double score;
    
        // Create an InpData from an std::map
        InpData(const std::map<std::string, std::string> & dmap) : name(dmap.at("name")),
                                                                   age(std::stoul(dmap.at("age"))),
                                                                   score(std::stod(dmap.at("score")))
        {}
    };
    

    Then you just have to read the data into an std::map<std::string, std::string> and the job is done.
    Such a function may be defined as follows:

    // Function to read data into an std::map
    std::map<std::string, std::string> read_data(std::istream & is)
    {
        std::map<std::string, std::string> data;
    
        std::string line, key, val;
        while(std::getline(is, line))
        {
            std::istringstream iss(line);
            if(std::getline(iss, key, '=') && std::getline(iss, val, '\n'))
                data[key] = val;
        }
    
        return data;
    }
    

    And it could then be used like below:

    int main()
    {
        // Input data stream (for the example)
        std::istringstream dstream("name=Ben\nage=21\nscore=4.65");
        
        // Create an InpData object from the returned std::map of the read_data() function
        InpData d(read_data(dstream));
    
        // Display (for validation)
        std::cout << d.name << " - " << d.age << " - " << d.score << '\n';
    
        return 0;
    }
    

    Live example here