This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Configuration Parser Submitted by

This is a configparser utility I've written. The config code uses some other small utilities I've made but they are rather boring. The config stuff however is somewhat cool. Maybe the code itself isn't the most beautiful piece of code ever created but I am pretty pleased with the interface that's presented to the user. It's very simple and you can give it whatever types you want, strings, ints, doubles, etc (check out main.cpp for an example). The only things the types need are a default constructor, an input operator and an output operator. It's also quite stable, it will not crash or anything if it encounters something that's wrong in the config file being parsed. Last but not least the config file will not be reformatted when you save the new values (try it out on config.txt that's included in the zip file). ArchMiffo
Send me a mail: archmiffo@darkbits.org
For more code and other cool stuff visit: http://darkbits.org

Currently browsing [configstuff.zip] (7,461 bytes) - [config code/archio.hpp] - (4,747 bytes)

    /* -----------------------------------------
      archio.hpp

      All the code in this file is open source
      so feel free to use it any way you would
      like.

      designed and implemented by
      ArchMiffo, member of Darkbits
    ----------------------------------------- */

    #ifndef AUL_ARCHIO_HPP
    #define AUL_ARCHIO_HPP

    /* -------------------
      Include directives
    ------------------- */
    #include <istream>
    #include <limits>
    #include <cctype>
    #include <algorithm>

    // aul stands for ArchMiffo Utility Library
    namespace aul
    {

    /* ---------------------------------------------------
      The archio is a work in progress. What you see
      here is just some stuff that I did to help me
      when I wrote the console code for another project.
      Most of the functions are nothing more tham a
      wrapper around basic istream funtions. The idea
      whith theese wrappers are that you will get a
      more flexible function that will work in more
      cases whithout having to think about the syntax.
      Hopefully I will add some more usefull functions
      to this library in the future since it ain't very
      interesting right now.
    --------------------------------------------------- */

    /* ----------------------------------
      IgnoreLine

      Ignores everything in the input
      stream until a linebreak is found
    ---------------------------------- */
    template<class charT, class traits>
    inline std::basic_istream<charT, traits>&
    IgnoreLine(std::basic_istream<charT, traits>& in)
    {
      in.ignore(std::numeric_limits<int>::max(), in.widen('\n'));
      return in;
    } // end IgnoreLine

    /* ---------------------------------------
      IgnoreTo

      A more generell form of IgnoreLine.
      This function could be used everywhere
      you use IgnoreLine but will also work
      if you want to ignore everything to
      some other character that you specify.
    --------------------------------------- */
    template<class charT, class traits>
    inline std::basic_istream<charT, traits>&
    IgnoreTo(std::basic_istream<charT, traits>& in, char delim)
    {
      in.ignore(std::numeric_limits<int>::max(), in.widen(delim));
      return in;
    } // end IgnoreLine

    /* ----------------------------------------------
      GetNextChar

      What this funtion does is return the next
      char that will be read from the input stream.
      Unlike peek this function skips all the
      leading whitespaces and returns an actual
      character (this is not all true becouse it
      could return a '\n' or something similar).
    ---------------------------------------------- */
    template<class charT, class traits>
    char GetNextChar(std::basic_istream<charT, traits>& in)
    {
      in>>std::ws;
      return in.peek();
    } // end GetNextChar

    /* --------------------------------------------------
      GetLine

      This is a rather pathetic wrapper that lets you
      get the a whole line up to a delimiter by writing
      about 7 charactersless than whithout the wrapper.
      The extra characters that you don't have to write
      gives you a function that will work in almost any
      case.
    -------------------------------------------------- */
    template<class charT, class traits>
    inline std::basic_istream<charT, traits>&
    GetLine(std::basic_istream<charT, traits>& in, std::string& str, char delim)
    {
      std::getline(in, str, in.widen(delim));
      return in;
    } // end GetLine

    /* -------------------------------------------
      RemoveSpace

      This function removes all space characters
      from a string
    ------------------------------------------- */
    inline void RemoveSpace(std::string& str)
    {
      str.erase(std::remove_if(str.begin(), str.end(), std::isspace), str.end());
    } // end RemoveSpace

    /* ---------------------------------------------------
      GetCommadVal

      This function was designed to be used as a part
      of a some config stuff I wrote. What it does is,
      given an input stream, extracts a command and it's
      value to the two strings that the user supplies.
      Example:
      This is in the stream: invertmouse = 1
      GetCommandVal(is, str1, str2, '=')
      => str1 = invertmouse, str2 = 1.
    --------------------------------------------------- */
    template<class charT, class traits>
    std::basic_istream<charT, traits>&
    GetCommandVal(std::basic_istream<charT, traits>& in,
                  std::string& command, std::string& val, char sep)
    {
      std::string str;
      in>>std::ws;
      GetLine(in, str, '\n');

      RemoveSpace(str);

      std::string::size_type index = str.find(sep);
      if(index == std::string::npos)
        throw "wrong seperator";

      command = std::string(str.begin(), str.begin() + index);
      val = std::string(str.begin() + index + 1, str.end());

      return in;
    } // end GetCommandVal

    } // end aul

    #endif // end AUL_ARCHIO_HPP

Currently browsing [configstuff.zip] (7,461 bytes) - [config code/configparser.cpp] - (6,020 bytes)

    /* -----------------------------------------
      configparser.cpp

      All the code in this file is open source
      so feel free to use it any way you would
      like.

      design and implementation by
      ArchMiffo, member of Darkbits
    ----------------------------------------- */

    /* -------------------
      Include directives
    ------------------- */
    #include "configparser.hpp"
    #include "archio.hpp"
    #include <sstream>
    #include <queue>
    #include <fstream>

    // aul stands for ArchMiffo Utility Library
    namespace aul
    {

    /* --------------------------------------
      ParseFile

      This is the main function that parses
      every line in the file
    -------------------------------------- */
    int ConfigParser::ParseFile(const std::string& filename)
    {
      m_Errors.clear();
      bool parseError = false;
      CFGError err;
      int line = 0;
      m_Filename = filename;
      std::ifstream file;
      file.open(m_Filename.c_str(), std::ios::in);

      if(file.fail()) // Couldn't open the file
        return CFG_ERROR::FILE_ERROR;

      std::string str;

      while(!file.eof())
      {
        aul::GetLine(file, str, '\n');
        ++line;

        if(str.empty())
          continue;

        if(str[0] == '#')
            continue;

        std::string::size_type index = str.find('=');
        if(index == std::string::npos)
        {
          err.line = line;
          err.code = CFG_ERROR::MISSING_SIGN_ERROR;
          m_Errors.push_back(err);
          parseError = true;
          continue;
        } // end if

        index = str.find('#');
        if(index != std::string::npos)
        {
          err.line = line;
          err.code = CFG_ERROR::COMMENT_ERROR;
          m_Errors.push_back(err);
          parseError = true;
          continue;
        } // end if

        std::string command, val;
        std::istringstream is(str);

        aul::GetCommandVal(is, command, val, '=');

        if(Initialize(command, val) != CFG_ERROR::SUCCESS)
        {
          err.line = line;
          err.code = CFG_ERROR::UNKNOWN_COMMAND_ERROR;
          m_Errors.push_back(err);
          parseError = true;
          continue;
        } // end if
      } // end while

      if(parseError)
        return CFG_ERROR::PARSE_ERROR;

      return CFG_ERROR::SUCCESS;
    } // end ConfigParser::ParseFile

    /* --------------------------------------------
      Initialize

      Checks if the command that were found exist
      and if it does it gives the right variable
      its new value
    -------------------------------------------- */
    int ConfigParser::Initialize(const std::string& command, const std::string& val)
    {
      ConfigMap::iterator it = m_ConfigItems.find(command);

      if(it == m_ConfigItems.end())
        return CFG_ERROR::UNKNOWN_COMMAND_ERROR;

      std::stringstream in(val);
      (it->second)->Read(in);

      return CFG_ERROR::SUCCESS;
    } // end ConfigParser::Initialize

    /* --------------------------------------------
      SaveFile

      Save the current settings. Doing this will
      not change the format of the config file.
      In other words the comments and everything
      will look exactly as before (except the new
      values of course)
    -------------------------------------------- */
    int ConfigParser::SaveFile()
    {
      std::queue<std::string> tempque;
      std::string str;
      ConfigMap::iterator it;

      {
        std::fstream file;
        file.open(m_Filename.c_str(), std::ios::out | std::ios::in);

        if(file.fail()) // Couldn't open the file
          return CFG_ERROR::FILE_ERROR;

        while(!file.eof())
        {
          aul::GetLine(file, str, '\n');
          tempque.push(str);
        } // end while
      } // end block

      std::fstream file;
      file.open(m_Filename.c_str(), std::ios::out | std::ios::trunc);

      if(file.fail()) // Couldn't open the file
        return CFG_ERROR::FILE_ERROR;

      while(!tempque.empty())
      {
        str = tempque.front();
        tempque.pop();

        std::string::size_type index = str.find('=');
        if(index == std::string::npos || str[0] == '#')
        {
          if(tempque.empty())
            file<<str;
          else
            file<<str<<'\n';
          continue;
        } // end if

        std::string::iterator strit = std::find_if(str.begin() + index, str.end(), std::isalnum);

        std::string command, val;
        std::istringstream is(str);

        aul::GetCommandVal(is, command, val, '=');

        it = m_ConfigItems.find(command);

        if(it == m_ConfigItems.end())
        {
          file<<str<<'\n';
          continue;
        } // end if

        std::string temp;
        std::ostringstream os(temp);
        (it->second)->Write(os);

        m_ConfigItems.erase(it);

        temp = os.str();

        str.erase(strit, str.end());
        str += temp;

        file<<str<<'\n';
      } // end while

      it = m_ConfigItems.begin();
      for(it; it != m_ConfigItems.end(); ++it)
      {
        file<<it->first<<'=';
        (it->second)->Write(file);
        file<<'\n';
      } // end for

      return CFG_ERROR::SUCCESS;
    } // end ConfigParser::SaveFile

    /* -----------------------------------------
      PrintErrors

      Print all the errors that occured during
      parsing of the config file
    ----------------------------------------- */
    void ConfigParser::PrintErrors(std::ostream& out)
    {
      if(m_Errors.empty())
      {
        out<<"No errors found in file "<<m_Filename<<std::endl;
        return;
      } // end if

      out<<"The following errors were found in "<<m_Filename<<":"<<std::endl;

      for(int i=0; i != m_Errors.size(); ++i)
      {
        out<<"error: "<<m_Errors[i].code<<"   line: "<<m_Errors[i].line<<'\t';

        switch(m_Errors[i].code)
        {
          case CFG_ERROR::COMMENT_ERROR :
            out<<"\'#\' sign not at start of line"<<std::endl;
            break;

          case CFG_ERROR::UNKNOWN_COMMAND_ERROR :
            out<<"Command could not be found"<<std::endl;
            break;

          case CFG_ERROR::MISSING_SIGN_ERROR :
            out<<"Missing \'=\' sign between the command and value"<<std::endl;
            break;

          default : break;
        } // end switch
      } // end for
    } // end ConfigParser::PrintErrors

    } // end aul

Currently browsing [configstuff.zip] (7,461 bytes) - [config code/configparser.hpp] - (3,515 bytes)

    /* -----------------------------------------
      configparser.hpp

      All the code in this file is open source
      so feel free to use it any way you would
      like.

      design and implementation by
      ArchMiffo, member of Darkbits
    ----------------------------------------- */

    #ifndef AUL_CONFIGPARSER_HPP
    #define AUL_CONFIGPARSER_HPP

    /* -------------------
      Include directives
    ------------------- */
    #include <string>
    #include <map>
    #include <vector>
    #include <iostream>
    #include "countedptr.hpp"

    // aul stands for ArchMiffo Utility Library
    namespace aul
    {

      /* ------------------------
        The CFG_ERROR namespace
      ------------------------ */
      namespace CFG_ERROR
      {
        // An enum for error codes
        enum
        {
          SUCCESS = 100,
          FILE_ERROR = 101,
          PARSE_ERROR = 102,
          COMMENT_ERROR = 103,
          UNKNOWN_COMMAND_ERROR = 104,
          MISSING_SIGN_ERROR = 105
        }; // end enum
      } // end CFG_ERROR

    /* --------------------------------
      The CFGError struct

      Used to store error information
    -------------------------------- */
    struct CFGError
    {
      int line;
      int code;
    }; // end CFGError

    /* --------------------------------------------------
      The BasicConfigItem class

      This is a base class that allows us to store any
      type of the template class ConfigItem in a vector
      or some other kind of container class
    -------------------------------------------------- */
    class BasicConfigItem
    {
    public :
      virtual ~BasicConfigItem() {}
      virtual std::istream& Read(std::istream& in) = 0;
      virtual std::ostream& Write(std::ostream& out) = 0;
    }; // end BasicConfigItem

    /* ------------------------------------------------
      The ConfigItem class

      This class is a placeholder for different types
      of variables. In the ConfigParser class we want
      to store any type to be read from the config
      file. To do this we make use of polymorphism
    ------------------------------------------------ */
    template<typename T>
    class ConfigItem : public BasicConfigItem
    {
    public :
      ConfigItem(T* item, T value = T())
      : m_Item(item) { *m_Item = value; }

      std::istream& Read(std::istream& in) { return in>>*m_Item; }
      std::ostream& Write(std::ostream& out) { return out<<*m_Item; }

    private :
      T* m_Item;
    }; // end ConfigItem

    /* ---------------------------------------------
      The ConfigParser class

      Whith this class you can save time by not
      having to write the same code over and over
      every time you want to have a config file.
      Another thing you don't have to do is change
      any config code if you want to add a new
      command in the config file. You simply use
      AddItem and the parse the file
    --------------------------------------------- */
    class ConfigParser
    {
    public :
      int ParseFile(const std::string& filename);
      int SaveFile();

      template<typename T>
      void AddItem(const std::string& command, T* item, T val=T())
      { m_ConfigItems[command] =
        aul::counted_ptr<BasicConfigItem>(new ConfigItem<T>(item, val)); }

      const std::vector<CFGError>& GetErrors() { return m_Errors; }
      void PrintErrors(std::ostream& out);

    private :
      int Initialize(const std::string& command, const std::string& val);

      typedef std::map<std::string, aul::counted_ptr<BasicConfigItem> > ConfigMap;
      ConfigMap m_ConfigItems;
      std::vector<CFGError> m_Errors;
      std::string m_Filename;
    }; // end ConfigParser

    } // end aul

    #endif // end AUL_CONFIGPARSER_HPP

Currently browsing [configstuff.zip] (7,461 bytes) - [config code/countedptr.hpp] - (3,203 bytes)

    /* -----------------------------------------
      countedptr.hpp

      All the code in this file is open source
      so feel free to use it any way you would
      like.

      designed and implemented by
      ArchMiffo, member of Darkbits
    ----------------------------------------- */

    #ifndef AUL_COUNTED_PTR_HPP
    #define AUL_COUNTED_PTR_HPP

    // aul stands for ArchMiffo Utility Library
    namespace aul
    {

    /* ------------------------------------------------------
      The counted_ptr class

      Acts like a real pointer but it keeps track of itīs
      pointee and deletes it when the last object that uses
      it goes out of scope. This is a very simple type of
      smart pointer but it does what it's supposed to do.
      Unlike the standard auto_ptr class you can use this
      pointer class in the standard containers (vector,
      list, etc) and you don't have to remember to free
      the memory when you are done with the pointer.
    ------------------------------------------------------ */
    template<typename T>
    class counted_ptr
    {
    public:
      // Some typedefs for accessing the
      // type of the pointer
      typedef T value_type;
      typedef T* pointer;
      typedef T& reference;

      explicit counted_ptr(T* ptr=0)
               : pointee_(ptr), refCount_(new int(1))
               {}

      ~counted_ptr() { decrease(); }

      counted_ptr(const counted_ptr<T>& cp)
      : pointee_(cp.pointee_), refCount_(cp.refCount_)
      { ++*refCount_; }

      // This constructor is used for implicit conversion
      // between diffrent types of counted_ptr
      template<typename U>
      counted_ptr(const counted_ptr<U>& cp)
      : pointee_(cp.pointee_), refCount_(cp.refCount_)
      { ++*refCount_; }

      counted_ptr<T>& operator=(const counted_ptr<T>& cp);

      // Conversion to regular pointer
      T* toPointer() { return pointee_; }

      T& operator*() { return *pointee_; }
      T* operator->() { return pointee_; }

      // To check for null pointer
      bool operator!() { return !pointee_; }

      int getCount() { return *refCount_; }

      T* extractPointer();

    private:
      // Decrease refcount and clean
      // up if this is the last object
      void decrease()
      {
        if(--*refCount_ == 0)
        {
          delete pointee_;
          delete refCount_;
        } // end if
      } // end decrease

      template<typename U>
      friend class counted_ptr;

      T* pointee_;
      int* refCount_;
    }; // end counted_ptr

    /* ----------------
      Copy assignment
    ---------------- */
    template<typename T>
    counted_ptr<T>& counted_ptr<T>::operator=(const counted_ptr<T>& cp)
    {
      if(pointee_ == cp.pointee_)
        return *this;

      decrease();

      pointee_ = cp.pointee_;
      refCount_ = cp.refCount_;

      ++*refCount_;

      return *this;
    } // end counted_ptr<T>::operator=

    /* --------------------------------------------------
      extractPointer

      Set the pointer to 0.
      After this the client is responsible for deleting
      the returned pointer
    -------------------------------------------------- */
    template<typename T>
    T* counted_ptr<T>::extractPointer()
    {
      T* ptr = pointee_;

      pointee_ = 0;

      return ptr;
    } // end counted_ptr<T>::extractPointer

    } // en namespace aul

    #endif // end AUL_COUNTED_PTR_HPP

Currently browsing [configstuff.zip] (7,461 bytes) - [config code/main.cpp] - (1,715 bytes)

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

    int main(int argc, char* argv[])
    {
      aul::ConfigParser cfgParser;

      bool invertMouse;
      bool showFPS;
      int width;
      int height;
      double number;
      std::string name;

      // This is all you need to do to use my config stuff
      // Add the things you want read...
      cfgParser.AddItem("invertmouse", &invertMouse, true);
      cfgParser.AddItem("showfps", &showFPS, true);
      cfgParser.AddItem("width", &width, 1024);
      cfgParser.AddItem("height", &height, 768);
      cfgParser.AddItem("number", &number, 42.0);
      cfgParser.AddItem("name", &name);

      // ...and parse the file. You don't have to bother
      // with the errors since the whole file will be parsed
      // anyway. The only reason you can check for errors
      // is in case you misspelled something or made some
      // other misstake in the config file
      int error = cfgParser.ParseFile("config.txt");

      // Handle the errors if you want to know what
      // went wrong
      if(error == aul::CFG_ERROR::PARSE_ERROR)
        cfgParser.PrintErrors(std::cout);
      else if(error == aul::CFG_ERROR::FILE_ERROR)
        std::cout<<"Unable to load cfg file";
      else if(error == aul::CFG_ERROR::SUCCESS)
        std::cout<<"Yeah";

      std::cout<<std::endl;
      std::cout<<std::endl;
      std::cout<<std::endl;

      std::cout<<"invertmouse: \t"<<invertMouse<<std::endl;
      std::cout<<"showfps: \t"<<showFPS<<std::endl;
      std::cout<<"width: \t\t"<<width<<std::endl;
      std::cout<<"height: \t"<<height<<std::endl;
      std::cout<<"number: \t"<<number<<std::endl;
      std::cout<<"name: \t\t"<<name<<std::endl;

      cfgParser.SaveFile();

      std::cin.get();

      return 0;
    }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.