See what's going on with flipcode!

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:
For more code and other cool stuff visit:

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

/* -----------------------------------------
  All the code in this file is open source
  so feel free to use it any way you would
  designed and implemented by
  ArchMiffo, member of Darkbits
----------------------------------------- */


/* ------------------- 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 [] (7,461 bytes) - [config code/configparser.cpp] - (6,020 bytes)

/* -----------------------------------------
  All the code in this file is open source
  so feel free to use it any way you would
  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;, std::ios::in);

if( // 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;, std::ios::out | std::ios::in);

if( // 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;, std::ios::out | std::ios::trunc);

if( // 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 [] (7,461 bytes) - [config code/configparser.hpp] - (3,515 bytes)

/* -----------------------------------------
  All the code in this file is open source
  so feel free to use it any way you would
  design and implementation by 
  ArchMiffo, member of Darkbits
----------------------------------------- */


/* ------------------- 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 [] (7,461 bytes) - [config code/countedptr.hpp] - (3,203 bytes)

/* -----------------------------------------
  All the code in this file is open source
  so feel free to use it any way you would
  designed and implemented by
  ArchMiffo, member of Darkbits
----------------------------------------- */


// 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;


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 [] (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.