1
0
Fork 0
mirror of https://github.com/cosmo-sims/monofonIC.git synced 2024-09-19 17:03:45 +02:00
monofonIC/include/config_file.hh

381 lines
12 KiB
C++
Raw Permalink Normal View History

2020-08-21 17:03:33 +02:00
// This file is part of monofonIC (MUSIC2)
// A software package to generate ICs for cosmological simulations
// Copyright (C) 2020 by Oliver Hahn
//
// monofonIC is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// monofonIC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2019-05-07 01:05:16 +02:00
#pragma once
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <logger.hh>
/*!
2020-04-04 20:55:24 +02:00
* @class config_file
2019-05-07 01:05:16 +02:00
* @brief provides read/write access to configuration options
*
* This class provides access to the configuration file. The
* configuration is stored in hash-pairs and can be queried and
* validated by the responsible class/routine
*/
2020-04-04 20:55:24 +02:00
class config_file {
2019-05-07 01:05:16 +02:00
//! current line number
2020-04-04 20:55:24 +02:00
unsigned iline_;
2019-05-07 01:05:16 +02:00
//! hash table for key/value pairs, stored as strings
2020-04-04 20:55:24 +02:00
std::map<std::string, std::string> items_;
2019-05-07 01:05:16 +02:00
public:
//! removes all white space from string source
/*!
* @param source the string to be trimmed
* @param delims a string of delimiting characters
* @return trimmed string
*/
std::string trim(std::string const &source,
char const *delims = " \t\r\n") const {
std::string result(source);
//... skip initial whitespace ...
std::string::size_type index = result.find_last_not_of(delims);
if (index != std::string::npos)
result.erase(++index);
//... find beginning of trailing whitespace ...
index = result.find_first_not_of(delims);
//... remove trailing whitespace ...
if (index != std::string::npos)
result.erase(0, index);
else
result.erase();
return result;
}
//! converts between different variable types
/*!
* The main purpose of this function is to parse and convert
* a string argument into numbers, booleans, etc...
* @param ival the input value (typically a std::string)
* @param oval the interpreted/converted value
*/
template <class in_value, class out_value>
2020-04-04 20:55:24 +02:00
void convert(const in_value &ival, out_value &oval) const {
2019-05-07 01:05:16 +02:00
std::stringstream ss;
ss << ival; //.. insert value into stream
ss >> oval; //.. retrieve value from stream
if (!ss.eof()) {
//.. conversion error
2020-04-04 20:27:51 +02:00
music::elog << "Error: conversion of \'" << ival << "\' failed."
2019-05-07 01:05:16 +02:00
<< std::endl;
2020-04-04 20:55:24 +02:00
throw except_invalid_conversion(std::string("invalid conversion to ") +
2019-05-07 01:05:16 +02:00
typeid(out_value).name() + '.');
}
}
//! constructor of class config_file
2020-04-04 20:55:24 +02:00
/*! @param filename the path/name of the configuration file to be parsed
2019-05-07 01:05:16 +02:00
*/
2020-04-04 20:55:24 +02:00
explicit config_file(std::string const &filename) : iline_(0), items_() {
std::ifstream file(filename.c_str());
2019-05-07 01:05:16 +02:00
if (!file.is_open()){
2020-04-04 20:55:24 +02:00
music::elog << "Could not open config file \'" << filename << "\'." << std::endl;
2019-05-07 01:05:16 +02:00
throw std::runtime_error(
2020-04-04 20:55:24 +02:00
std::string("Error: Could not open config file \'") + filename +
2019-05-07 01:05:16 +02:00
std::string("\'"));
}
std::string line;
std::string name;
std::string value;
2020-04-04 20:55:24 +02:00
std::string in_section;
int pos_equal;
iline_ = 0;
2019-05-07 01:05:16 +02:00
//.. walk through all lines ..
while (std::getline(file, line)) {
2020-04-04 20:55:24 +02:00
++iline_;
2019-05-07 01:05:16 +02:00
//.. encounterd EOL ?
if (!line.length())
continue;
//.. encountered comment ?
unsigned long idx;
if ((idx = line.find_first_of("#;%")) != std::string::npos)
line.erase(idx);
//.. encountered section tag ?
if (line[0] == '[') {
2020-04-04 20:55:24 +02:00
in_section = trim(line.substr(1, line.find(']') - 1));
2019-05-07 01:05:16 +02:00
continue;
}
//.. seek end of entry name ..
2020-04-04 20:55:24 +02:00
pos_equal = line.find('=');
name = trim(line.substr(0, pos_equal));
value = trim(line.substr(pos_equal + 1));
2019-05-07 01:05:16 +02:00
2020-04-04 20:55:24 +02:00
if ((size_t)pos_equal == std::string::npos &&
2019-05-07 01:05:16 +02:00
(name.size() != 0 || value.size() != 0)) {
2020-04-04 20:55:24 +02:00
music::wlog << "Ignoring non-assignment in " << filename << ":"
<< iline_ << std::endl;
2019-05-07 01:05:16 +02:00
continue;
}
if (name.length() == 0 && value.size() != 0) {
2020-04-04 20:27:51 +02:00
music::wlog << "Ignoring assignment missing entry name in "
2020-04-04 20:55:24 +02:00
<< filename << ":" << iline_ << std::endl;
2019-05-07 01:05:16 +02:00
continue;
}
if (value.length() == 0 && name.size() != 0) {
2020-04-04 20:55:24 +02:00
music::wlog << "Empty entry will be ignored in " << filename << ":"
<< iline_ << std::endl;
2019-05-07 01:05:16 +02:00
continue;
}
if (value.length() == 0 && name.size() == 0)
continue;
//.. add key/value pair to hash table ..
2020-04-04 20:55:24 +02:00
if (items_.find(in_section + '/' + name) != items_.end()) {
2020-04-04 20:27:51 +02:00
music::wlog << "Redeclaration overwrites previous value in "
2020-04-04 20:55:24 +02:00
<< filename << ":" << iline_ << std::endl;
2019-05-07 01:05:16 +02:00
}
2020-04-04 20:55:24 +02:00
items_[in_section + '/' + name] = value;
2019-05-07 01:05:16 +02:00
}
}
//! inserts a key/value pair in the hash map
/*! @param key the key value, usually "section/key"
* @param value the value of the key, also a string
*/
2020-04-04 20:55:24 +02:00
void insert_value(std::string const &key, std::string const &value) {
items_[key] = value;
2019-05-07 01:05:16 +02:00
}
//! inserts a key/value pair in the hash map
/*! @param section section name. values are stored under "section/key"
* @param key the key value usually "section/key"
* @param value the value of the key, also a string
*/
2020-04-04 20:55:24 +02:00
void insert_value(std::string const &section, std::string const &key,
2019-05-07 01:05:16 +02:00
std::string const &value) {
2020-04-04 20:55:24 +02:00
items_[section + '/' + key] = value;
2019-05-07 01:05:16 +02:00
}
//! checks if a key is part of the hash map
/*! @param section the section name of the key
* @param key the key name to be checked
* @return true if the key is present, false otherwise
*/
2020-04-04 20:55:24 +02:00
bool contains_key(std::string const &section, std::string const &key) {
2019-05-07 01:05:16 +02:00
std::map<std::string, std::string>::const_iterator i =
2020-04-04 20:55:24 +02:00
items_.find(section + '/' + key);
if (i == items_.end())
2019-05-07 01:05:16 +02:00
return false;
return true;
}
//! checks if a key is part of the hash map
/*! @param key the key name to be checked
* @return true if the key is present, false otherwise
*/
2020-04-04 20:55:24 +02:00
bool contains_key(std::string const &key) {
std::map<std::string, std::string>::const_iterator i = items_.find(key);
if (i == items_.end())
2019-05-07 01:05:16 +02:00
return false;
return true;
}
//! return value of a key
2020-04-04 20:55:24 +02:00
/*! returns the value of a given key, throws a except_item_not_found
2019-05-07 01:05:16 +02:00
* exception if the key is not available in the hash map.
* @param key the key name
* @return the value of the key
2020-04-04 20:55:24 +02:00
* @sa except_item_not_found
2019-05-07 01:05:16 +02:00
*/
2020-04-04 20:55:24 +02:00
template <class T> T get_value(std::string const &key) const {
return get_value<T>("", key);
2019-05-07 01:05:16 +02:00
}
//! return value of a key
2020-04-04 20:55:24 +02:00
/*! returns the value of a given key, throws a except_item_not_found
2019-05-07 01:05:16 +02:00
* exception if the key is not available in the hash map.
* @param section the section name for the key
* @param key the key name
* @return the value of the key
2020-04-04 20:55:24 +02:00
* @sa except_item_not_found
2019-05-07 01:05:16 +02:00
*/
template <class T>
2020-04-04 20:55:24 +02:00
T get_value_basic(std::string const &section, std::string const &key) const {
2019-05-07 01:05:16 +02:00
T r;
std::map<std::string, std::string>::const_iterator i =
2020-04-04 20:55:24 +02:00
items_.find(section + '/' + key);
if (i == items_.end()){
throw except_item_not_found('\'' + section + '/' + key +
2019-05-07 01:05:16 +02:00
std::string("\' not found."));
}
2020-04-04 20:55:24 +02:00
convert(i->second, r);
2019-05-07 01:05:16 +02:00
return r;
}
template <class T>
2020-04-04 20:55:24 +02:00
T get_value(std::string const &section, std::string const &key) const
2019-05-07 01:05:16 +02:00
{
T r;
try
{
2020-04-04 20:55:24 +02:00
r = get_value_basic<T>(section, key);
2019-05-07 01:05:16 +02:00
}
2020-04-04 20:55:24 +02:00
catch (except_item_not_found& e)
2019-05-07 01:05:16 +02:00
{
2020-04-04 20:27:51 +02:00
music::elog << e.what() << std::endl;
2019-05-07 01:05:16 +02:00
throw;
}
return r;
}
//! exception safe version of getValue
/*! returns the value of a given key, returns a default value rather
2020-04-04 20:55:24 +02:00
* than a except_item_not_found exception if the key is not found.
2019-05-07 01:05:16 +02:00
* @param section the section name for the key
* @param key the key name
* @param default_value the value that is returned if the key is not found
* @return the key value (if key found) otherwise default_value
*/
template <class T>
2020-04-04 20:55:24 +02:00
T get_value_safe(std::string const &section, std::string const &key,
2019-05-07 01:05:16 +02:00
T default_value) const {
T r;
try {
2020-04-04 20:55:24 +02:00
r = get_value_basic<T>(section, key);
} catch (except_item_not_found&) {
2019-05-07 01:05:16 +02:00
r = default_value;
2020-04-04 20:55:24 +02:00
music::dlog << "Item \'" << section << "/" << key << " not found in config. Default = \'" << default_value << "\'" << std::endl;
2019-05-07 01:05:16 +02:00
}
return r;
}
//! exception safe version of getValue
/*! returns the value of a given key, returns a default value rather
2020-04-04 20:55:24 +02:00
* than a except_item_not_found exception if the key is not found.
2019-05-07 01:05:16 +02:00
* @param key the key name
* @param default_value the value that is returned if the key is not found
* @return the key value (if key found) otherwise default_value
*/
template <class T>
2020-04-04 20:55:24 +02:00
T get_value_safe(std::string const &key, T default_value) const {
return get_value_safe("", key, default_value);
2019-05-07 01:05:16 +02:00
}
//! dumps all key-value pairs to a std::ostream
2020-04-04 20:55:24 +02:00
void dump(std::ostream &out) {
std::map<std::string, std::string>::const_iterator i = items_.begin();
while (i != items_.end()) {
2019-05-07 01:05:16 +02:00
if (i->second.length() > 0)
out << std::setw(24) << std::left << i->first << " = " << i->second
<< std::endl;
++i;
}
}
2020-04-04 20:55:24 +02:00
void dump_to_log(void) {
2020-04-04 20:27:51 +02:00
music::ilog << "List of all configuration options:" << std::endl;
2020-04-04 20:55:24 +02:00
std::map<std::string, std::string>::const_iterator i = items_.begin();
while (i != items_.end()) {
2019-05-07 01:05:16 +02:00
if (i->second.length() > 0)
2020-04-04 20:27:51 +02:00
music::ilog << std::setw(28) << i->first << " = " << i->second
2019-05-07 01:05:16 +02:00
<< std::endl;
++i;
}
}
//--- EXCEPTIONS ---
//! runtime error that is thrown if key is not found in getValue
2020-04-04 20:55:24 +02:00
class except_item_not_found : public std::runtime_error {
2019-05-07 01:05:16 +02:00
public:
2020-04-04 20:55:24 +02:00
except_item_not_found(std::string itemname)
2019-05-07 01:05:16 +02:00
: std::runtime_error(itemname.c_str()) {}
};
//! runtime error that is thrown if type conversion fails
2020-04-04 20:55:24 +02:00
class except_invalid_conversion : public std::runtime_error {
2019-05-07 01:05:16 +02:00
public:
2020-04-04 20:55:24 +02:00
except_invalid_conversion(std::string errmsg) : std::runtime_error(errmsg) {}
2019-05-07 01:05:16 +02:00
};
//! runtime error that is thrown if identifier is not found in keys
class ErrIllegalIdentifier : public std::runtime_error {
public:
ErrIllegalIdentifier(std::string errmsg) : std::runtime_error(errmsg) {}
};
};
//==== below are template specialisations
//=======================================//
//... Function: getValue( strSection, strEntry ) ...
//... Descript: specialization of getValue for type boolean to interpret strings
//...
//... like "true" and "false" etc.
//... converts the string to type bool, returns type bool ...
template <>
2020-04-04 20:55:24 +02:00
inline bool config_file::get_value<bool>(std::string const &strSection,
2019-05-07 01:05:16 +02:00
std::string const &strEntry) const {
2020-04-04 20:55:24 +02:00
std::string r1 = get_value<std::string>(strSection, strEntry);
2019-05-07 01:05:16 +02:00
if (r1 == "true" || r1 == "yes" || r1 == "on" || r1 == "1")
return true;
if (r1 == "false" || r1 == "no" || r1 == "off" || r1 == "0")
return false;
2020-04-04 20:27:51 +02:00
music::elog << "Illegal identifier \'" << r1 << "\' in \'" << strEntry << "\'." << std::endl;
2019-05-07 01:05:16 +02:00
throw ErrIllegalIdentifier(std::string("Illegal identifier \'") + r1 +
std::string("\' in \'") + strEntry +
std::string("\'."));
// return false;
}
template <>
2020-04-04 20:55:24 +02:00
inline bool config_file::get_value_safe<bool>(std::string const &strSection,
2019-05-07 01:05:16 +02:00
std::string const &strEntry,
bool defaultValue) const {
std::string r1;
try {
2020-04-04 20:55:24 +02:00
r1 = get_value_basic<std::string>(strSection, strEntry);
2019-05-07 01:05:16 +02:00
if (r1 == "true" || r1 == "yes" || r1 == "on" || r1 == "1")
return true;
if (r1 == "false" || r1 == "no" || r1 == "off" || r1 == "0")
return false;
2020-04-04 20:55:24 +02:00
} catch (except_item_not_found&) {
2019-05-07 01:05:16 +02:00
return defaultValue;
}
return defaultValue;
}
template <>
inline void
2020-04-04 20:55:24 +02:00
config_file::convert<std::string, std::string>(const std::string &ival,
2019-05-07 01:05:16 +02:00
std::string &oval) const {
oval = ival;
}