// ConfigFile.h | |
// Class for reading named values from configuration files | |
// Richard J. Wagner v2.1 24 May 2004 wagnerr@umich.edu | |
// Copyright (c) 2004 Richard J. Wagner | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to | |
// deal in the Software without restriction, including without limitation the | |
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
// sell copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
// IN THE SOFTWARE. | |
// Typical usage | |
// ------------- | |
// | |
// Given a configuration file "settings.inp": | |
// atoms = 25 | |
// length = 8.0 # nanometers | |
// name = Reece Surcher | |
// | |
// Named values are read in various ways, with or without default values: | |
// ConfigFile config( "settings.inp" ); | |
// int atoms = config.read<int>( "atoms" ); | |
// double length = config.read( "length", 10.0 ); | |
// string author, title; | |
// config.readInto( author, "name" ); | |
// config.readInto( title, "title", string("Untitled") ); | |
// | |
// See file example.cpp for more examples. | |
#ifndef CONFIGFILE_H | |
#define CONFIGFILE_H | |
#include <string> | |
#include <map> | |
#include <iostream> | |
#include <fstream> | |
#include <sstream> | |
using std::string; | |
class ConfigFile { | |
// Data | |
protected: | |
string myDelimiter; // separator between key and value | |
string myComment; // separator between value and comments | |
string mySentry; // optional string to signal end of file | |
std::map<string,string> myContents; // extracted keys and values | |
typedef std::map<string,string>::iterator mapi; | |
typedef std::map<string,string>::const_iterator mapci; | |
// Methods | |
public: | |
ConfigFile( string filename, | |
string delimiter = "=", | |
string comment = "#", | |
string sentry = "EndConfigFile" ); | |
ConfigFile(); | |
// Search for key and read value or optional default value | |
template<class T> T read( const string& key ) const; // call as read<T> | |
template<class T> T read( const string& key, const T& value ) const; | |
template<class T> bool readInto( T& var, const string& key ) const; | |
template<class T> | |
bool readInto( T& var, const string& key, const T& value ) const; | |
// Modify keys and values | |
template<class T> void add( string key, const T& value ); | |
void remove( const string& key ); | |
// Check whether key exists in configuration | |
bool keyExists( const string& key ) const; | |
// Check or change configuration syntax | |
string getDelimiter() const { return myDelimiter; } | |
string getComment() const { return myComment; } | |
string getSentry() const { return mySentry; } | |
string setDelimiter( const string& s ) | |
{ string old = myDelimiter; myDelimiter = s; return old; } | |
string setComment( const string& s ) | |
{ string old = myComment; myComment = s; return old; } | |
// Write or read configuration | |
friend std::ostream& operator<<( std::ostream& os, const ConfigFile& cf ); | |
friend std::istream& operator>>( std::istream& is, ConfigFile& cf ); | |
protected: | |
template<class T> static string T_as_string( const T& t ); | |
template<class T> static T string_as_T( const string& s ); | |
static void trim( string& s ); | |
// Exception types | |
public: | |
struct file_not_found { | |
string filename; | |
file_not_found( const string& filename_ = string() ) | |
: filename(filename_) {} }; | |
struct key_not_found { // thrown only by T read(key) variant of read() | |
string key; | |
key_not_found( const string& key_ = string() ) | |
: key(key_) {} }; | |
}; | |
/* static */ | |
template<class T> | |
string ConfigFile::T_as_string( const T& t ) | |
{ | |
// Convert from a T to a string | |
// Type T must support << operator | |
std::ostringstream ost; | |
ost << t; | |
return ost.str(); | |
} | |
/* static */ | |
template<class T> | |
T ConfigFile::string_as_T( const string& s ) | |
{ | |
// Convert from a string to a T | |
// Type T must support >> operator | |
T t; | |
std::istringstream ist(s); | |
ist >> t; | |
return t; | |
} | |
/* static */ | |
template<> | |
inline string ConfigFile::string_as_T<string>( const string& s ) | |
{ | |
// Convert from a string to a string | |
// In other words, do nothing | |
return s; | |
} | |
/* static */ | |
template<> | |
inline bool ConfigFile::string_as_T<bool>( const string& s ) | |
{ | |
// Convert from a string to a bool | |
// Interpret "false", "F", "no", "n", "0" as false | |
// Interpret "true", "T", "yes", "y", "1", "-1", or anything else as true | |
bool b = true; | |
string sup = s; | |
for( string::iterator p = sup.begin(); p != sup.end(); ++p ) | |
*p = toupper(*p); // make string all caps | |
if( sup==string("FALSE") || sup==string("F") || | |
sup==string("NO") || sup==string("N") || | |
sup==string("0") || sup==string("NONE") ) | |
b = false; | |
return b; | |
} | |
template<class T> | |
T ConfigFile::read( const string& key ) const | |
{ | |
// Read the value corresponding to key | |
mapci p = myContents.find(key); | |
if( p == myContents.end() ) throw key_not_found(key); | |
return string_as_T<T>( p->second ); | |
} | |
template<class T> | |
T ConfigFile::read( const string& key, const T& value ) const | |
{ | |
// Return the value corresponding to key or given default value | |
// if key is not found | |
mapci p = myContents.find(key); | |
if( p == myContents.end() ) return value; | |
return string_as_T<T>( p->second ); | |
} | |
template<class T> | |
bool ConfigFile::readInto( T& var, const string& key ) const | |
{ | |
// Get the value corresponding to key and store in var | |
// Return true if key is found | |
// Otherwise leave var untouched | |
mapci p = myContents.find(key); | |
bool found = ( p != myContents.end() ); | |
if( found ) var = string_as_T<T>( p->second ); | |
return found; | |
} | |
template<class T> | |
bool ConfigFile::readInto( T& var, const string& key, const T& value ) const | |
{ | |
// Get the value corresponding to key and store in var | |
// Return true if key is found | |
// Otherwise set var to given default | |
mapci p = myContents.find(key); | |
bool found = ( p != myContents.end() ); | |
if( found ) | |
var = string_as_T<T>( p->second ); | |
else | |
var = value; | |
return found; | |
} | |
template<class T> | |
void ConfigFile::add( string key, const T& value ) | |
{ | |
// Add a key with given value | |
string v = T_as_string( value ); | |
trim(key); | |
trim(v); | |
myContents[key] = v; | |
return; | |
} | |
#endif // CONFIGFILE_H | |
// Release notes: | |
// v1.0 21 May 1999 | |
// + First release | |
// + Template read() access only through non-member readConfigFile() | |
// + ConfigurationFileBool is only built-in helper class | |
// | |
// v2.0 3 May 2002 | |
// + Shortened name from ConfigurationFile to ConfigFile | |
// + Implemented template member functions | |
// + Changed default comment separator from % to # | |
// + Enabled reading of multiple-line values | |
// | |
// v2.1 24 May 2004 | |
// + Made template specializations inline to avoid compiler-dependent linkage | |
// + Allowed comments within multiple-line values | |
// + Enabled blank line termination for multiple-line values | |
// + Added optional sentry to detect end of configuration file | |
// + Rewrote messy trimWhitespace() function as elegant trim() |