Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Config Parser  (Read 3808 times)

0 Members and 1 Guest are viewing this topic.

matkod

  • Newbie
  • *
  • Posts: 5
    • View Profile
Config Parser
« on: March 29, 2013, 01:44:50 am »
Hi,
I made .cfg parser based on this: https://github.com/SFML/SFML/wiki/Source%3A-Settings-Parser
the basic diference is that mine is divided by sectors  ;D

Its not great but i would like to share  ;)

sorry if its not the right place to post it.

EDIT: now has comment support

TODO: add vector<string> support

Convert.hpp
#ifndef _CONVERT_HPP
#define _CONVERT_HPP

#include <string>
#include <sstream>

namespace Convert
{
//public:
   // Convert T, which should be a primitive, to a std::string.
   template <typename T>
   static std::string T_to_string(T const &val)
   {
      std::ostringstream ostr;
      ostr << val;

      return ostr.str();
   }
   
   // Convert a std::string to T.
   template <typename T>
   static T string_to_T(std::string const &val)
   {
      std::istringstream istr(val);
      T returnVal;
      if (!(istr >> returnVal))
         exit(EXIT_FAILURE);

      return returnVal;
   }

   template <>
   static std::string string_to_T(std::string const &val)
   {
      return val;
   }

};

#endif

ConfigParser.hpp
#ifndef _CONFIGPARSER_HPP
#define _CONFIGPARSER_HPP

#include <string>
#include <map>
#include <vector>

#include "Convert.hpp"

typedef std::map< const std::string, std::pair< std::string, std::string > > DataFormat;
typedef std::map< const std::string, DataFormat > CfgFormat;


class ConfigParser
{
public:
        ConfigParser(char comsep='#');
        ~ConfigParser();

        bool Read(const std::string& filename);

        bool Write(const std::string& filename);

        bool SectionExists(const std::string& section) const;
       
        // Only for primitives
        template <typename TYPE>
        TYPE Get(const std::string& section, const std::string& key, const TYPE& defValue=TYPE()) const
        {
                if(SectionExists(section))
                        if(mProperties.at(section).count(key))
                                return Convert::string_to_T<TYPE>(mProperties.at(section).at(key).first);

                return defValue;
        };

        // Only for primitives
        template <typename TYPE>
        void Set(const std::string& section, const std::string& key, const TYPE value, const std::string& comment="")
        {
                mIsChanged = true;
               
                if(!SectionExists(section))
                {
                        DataFormat prop;
                        prop[key] = std::make_pair(Convert::T_to_string(value), comment);
                        mProperties[section] = prop;
                        mOrder.push_back(section);
                }
                else
                {
                        mProperties[section][key].first = Convert::T_to_string(value);
                        if(comment.size())
                        {
                                mProperties[section][key].second = comment;
                        }
                }
        };
       
private:
        CfgFormat mProperties;

        std::vector<const std::string> mOrder;

        bool mIsChanged;

        std::string mFileName;

        char mComSep;
};

#endif

ConfigParser.cpp
#include <fstream>
#include <iostream>

#include "ConfigParser.hpp"

ConfigParser::ConfigParser(char comsep):
        mProperties(),
        mOrder(),
        mIsChanged(false),
        mFileName("config.cfg"),
        mComSep(comsep)
{

}


ConfigParser::~ConfigParser()
{
        if(mIsChanged)
                Write(mFileName);
}

bool ConfigParser::Read(const std::string& filename)
{
        mFileName = filename;

        std::ifstream in(filename.c_str());

        if(!in.is_open())
        {
                std::cerr << "error at reading" << std::endl;
                return false;
        }

        std::string line, section, value, param, comment;

        while(!in.eof())
        {
                char temp[256];
                in.getline(temp, 256);
                line.assign(temp);

                size_t lstart = line.find_first_not_of(' '); // remove whitespace before text

                if(line.size() > 0 && lstart != line.npos)
                {
                        if(line[lstart]=='[') // check for new section
                        {
                                size_t start = line.find_first_not_of(' ', lstart+1); // remove whitespace before text. +1 to ignore '['

                                size_t end = line.find_first_of(" ]", start); // get the text until a ']' or whitespace

                                end -= start; // from start to end

                                section = line.substr(start, end);

                                param = "";
                                value = "";
                                comment = "";
                        }
                        else if(line[lstart]==mComSep) //check for comment
                        {
                                mOrder.push_back(line.substr(lstart));
                        }
                        else // check for param and value
                        {
                                //get param
                                size_t end = line.find_first_of(" =", lstart);
                               
                                end -= lstart;

                                param = line.substr(lstart, end);

                                // get value
                                size_t start = line.find_first_of("=", lstart);
                                start++;
                                start = line.find_first_not_of(' ', start);
                               
                                end = line.find_first_of(mComSep, start);

                                end -= start;

                                value = line.substr(start, end);

                                start = line.find_first_of(mComSep, lstart);

                                if(start!=line.npos)
                                        comment = line.substr(start);
                                else
                                        comment = "";
                        }
                       
                }

                if(section.size())
                {
                        if(SectionExists(section))
                        {
                                if(param.size() && value.size())
                                        mProperties.at(section).insert(std::make_pair(param, std::make_pair(value, comment)));
                        }
                        else // the first time it will come here
                        {
                                DataFormat prop;
                                mProperties.insert(std::make_pair(section, prop));
                                mOrder.push_back(section);
                        }
                }

        }

        in.close();

        return true;
}

bool ConfigParser::Write(const std::string& filename)
{
        std::ofstream out(filename);

        if(!out.is_open())
        {
                std::cerr << "error at writing" << std::endl;
                return false;
        }

        std::string section = "";

        std::vector<const std::string>::const_iterator iter;
        for(iter=mOrder.begin(); iter!=mOrder.end(); ++iter)
        {
                if((*iter)[0] == mComSep)
                        out << (*iter) << std::endl;
                else
                {
                        if(section!=(*iter))
                        {
                                section = (*iter);

                                out << "[" << section << "]" << std::endl;
                        }

                        DataFormat::const_iterator iter2;
                        for(iter2=mProperties.at(*iter).begin(); iter2!=mProperties.at(*iter).end(); ++iter2)
                        {
                                if(iter2->second.second.size())
                                        out << iter2->first << " = " << iter2->second.first << " " << iter2->second.second << std::endl;
                                else
                                        out << iter2->first << " = " << iter2->second.first << std::endl;
                        }

                        out << std::endl;
                }
        }

        out.close();

        mIsChanged = false;

        return true;
}

bool ConfigParser::SectionExists(const std::string& section) const
{
        return mProperties.count(section) == 1;
}

 

main.cpp
#include <iostream>

#include "ConfigParser.hpp"


int main(void)
{
        ConfigParser cp;

        cp.Read("teste.cfg");

        cp.Set<int>("window1", "width1", 800);
       
        cp.Set<bool>("newSector", "newBool", true);
        cp.Set<bool>("newSector", "newBool2", false);
        cp.Set<int>("newSector", "newInt", 49);
        cp.Set<int>("newSector", "newInt2", 1000);

        cp.Set<float>("newSector2", "newFloat", 1.73);
        cp.Set<float>("newSector2", "newFloat", 2.499);
        cp.Set<double>("newSector2", "newDouble", 0.012);
        cp.Set<std::string>("newSector2", "newString", "I'm a big string of text!!!", "#change the comment");

        std::cout << cp.Get<bool>("teste", "bool") << std::endl;
        std::cout << cp.Get<int>("window", "width") << std::endl;
        std::cout << cp.Get<float>("newSector2", "newFloat") << std::endl;
        std::cout << cp.Get<int>("Invalid", "Invalid", 27) << std::endl; // invalid does not exist so it will return 27
        std::cout << cp.Get<std::string>("newSector2", "newString", "string does not exist") << std::endl;
       
        cp.Write("teste.cfg");

       
        system("pause");
        return 0;
}
 

teste.cfg
#Welcome!!!
#this is a comment
#
#

[window]
bpp = 32         #blablabla
height = 200          # comment 1
vsync = 0             ##other comment
width = 100              ###### yet another comment

#this is my second window config
[window1]
width1 = 800#nospace

# sound stuff
[sound]
fx = 1.0        #  max volume is 1.0
music = 0.5

[teste]
bool = 1

[teste2]
#teste2 is empty
« Last Edit: March 29, 2013, 06:24:05 pm by matkod »

mateandmetal

  • Full Member
  • ***
  • Posts: 171
  • The bird is the word
    • View Profile
    • my blog
Re: Config Parser
« Reply #1 on: March 29, 2013, 03:18:46 am »
- why do you include <cstring> everywhere?
- why do you declare and implement empty destructor?

- Mate (beverage) addict
- Heavy metal addict _lml
- SFML 2 addict
- My first (and free) game: BichingISH!

matkod

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Config Parser
« Reply #2 on: March 29, 2013, 03:54:58 am »
Because I forgot that <cstring> is the <string.h> equivalent   :-[ :-[ :-[

I was going to call Write on destructor if something was changed. thx  ::)
« Last Edit: March 29, 2013, 04:49:10 am by matkod »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11033
    • View Profile
    • development blog
    • Email
AW: Config Parser
« Reply #3 on: March 29, 2013, 12:46:23 pm »
Just use <string>. <cstring> and <string.h> are equivalent, except that the cstring header puts everthing into the std namespace, but both headers are from C, so you should go the C++ way with <string>. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

matkod

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Config Parser
« Reply #4 on: March 29, 2013, 05:08:30 pm »
Now it can read and write comments =D