HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Simple ini file parser

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
parserfilesimpleini

Problem

The below is a C++ parser for a reasonably simple INI grammar (although my understanding is that there isn't an official spec as such).

My grammar is roughly:

comments: (optional whitespace){;#}
section: (optional whitespace)[{printable ASCII}]
keyval: (optional whitespace){nows printable ASCII}={printable ASCII}


ini_parser.h

#pragma once

#include 
#include 

class IniParser {

public:
    IniParser(const std::string &path);

    template
    T get(const std::string &key, const std::string §ion);

    //get values in 'default' section
    template
    T get(const std::string &key) { return get(key, ""); }

private:
    using SectionMap = std::map;
    using IniMap = std::map;

    IniMap inimap;
};


ini_parser.cpp

```
#include
#include
#include
#include
#include
#include
#include "ini_parser.h"

namespace {
bool iskey(char c){
return isalnum(c) || ispunct(c);
}

bool isval(char c){
return isalnum(c) || ispunct(c) || isblank(c);
}
}

IniParser::IniParser(const std::string &path) {

enum class State {
init,
section,
key,
value,
skipline
} state = State::init;

std::ifstream f{path, std::ios::in};
if(!f.is_open()){
throw std::runtime_error("file doesn't exist");
}

std::vector buf;
std::string section, key;
char c;

auto err_helper = &{
std::ostringstream ss;
ss (c)
std::string IniParser::get(const std::string &key, const std::string §ion){
return inimap.at(section).at(key);
}

template<>
int IniParser::get(const std::string &key, const std::string §ion){
return std::stoi(inimap.at(section).at(key));
}

template<>
std::uint16_t IniParser::get(const std::string &key, const std::string §ion){
auto val = std::stoul(inimap.at(section).at(key));
if(val > std::numeric_limits::max()){
throw std::overflow_error("value too large for type");
}
return static_cast(v

Solution

I see some things that may help you improve your code.

Consider altering the grammar

Right now the grammar will accept a line like this:

[user]Not a comment, but acts like one


But explicitly reject lines like this one:

key = value


The reason is that the space between the key and the = causes the parser to throw a key error. That's a bit inconvenient and could be remedied by adding an additional state between the key and = that would exlicitly allow whitespace there.

Consider reordering slightly

Instead of this series of statements:

const std::string token = std::string(buf.begin(), buf.end());
buf.clear();
inimap[section][key] = token;


One might instead consider this:

inimap[section][key] = std::string(buf.begin(), buf.end());
buf.clear();


A smart compiler might be able to generate the same code, but the latter version means that the compiler might better be able to do a move of the string rather than a create/copy/delete.

Reconsider the interface

While I can see the appeal of a generic get interface, everything is stored internally as a std::string, so it might make sense to simplify the interface of this class to only return a std::string and let the recieving code do any conversions. This has the advantage that the conversion routines are no longer part of the class and may be customized by usage or perhaps by key. For example, a custom date class might benefit from having, essentially, a custom converter from std::string which may well be useful outside the IniParser class (as with retrieving the data from a user and then converting).

Consider finer grained error handling

The int version of get has this single line as its body:

return std::stoi(inimap.at(section).at(key));


This single line has three different ways to throw a std::out_of_range error. The section lookup could fail, or the key lookup could fail or stoi could fail. It will not be easy for the calling code to determine which of these failures occurred if one does. It's possible that the calling code only needs to know that an error occured and not which one, but a finer-grained error reporting mechanism (e.g. with custom error classes) might help if the calling code would benefi from knowing the difference between a key lookup error and a section lookup error.

Use std::regex

For an approach which uses std::regex, see ini file parser in C++

Code Snippets

[user]Not a comment, but acts like one
key = value
const std::string token = std::string(buf.begin(), buf.end());
buf.clear();
inimap[section][key] = token;
inimap[section][key] = std::string(buf.begin(), buf.end());
buf.clear();
return std::stoi(inimap.at(section).at(key));

Context

StackExchange Code Review Q#135887, answer score: 2

Revisions (0)

No revisions yet.