  | 
   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.
 
 
 |