// // Software License for AMDiS // // Copyright (c) 2010 Dresden University of Technology // All rights reserved. // Authors: Simon Vey, Thomas Witkowski et al. // // This file is part of AMDiS // // See also license.opensource.txt in the distribution. #ifndef INITFILE_H #define INITFILE_H #include #include #include #include #include #include #include #include #include #include #include "FixVec.h" #include #include #include #include // a parser for arithmetic expressions #include "muParser.h" namespace AMDiS { namespace InitfileInternal { /// Exceptions struct WrongVectorSize : std::runtime_error { WrongVectorSize(std::string m) : std::runtime_error(m) {} }; struct NoDelim : std::runtime_error { NoDelim(std::string m) : std::runtime_error(m) {} }; struct WrongVectorFormat : std::runtime_error { WrongVectorFormat(std::string m) : std::runtime_error(m) {} }; struct GetTagError : std::runtime_error { GetTagError(std::string m) : std::runtime_error(m) {} }; template struct WrongValueFormat : std::runtime_error { static std::string name(int) { return "int"; } static std::string name(bool) { return "bool"; } static std::string name(double) { return "double"; } static std::string name(unsigned int) { return "unsigned int"; } template static std::string name(G) { return std::string(typeid(G).name()); } WrongValueFormat(std::string value) : std::runtime_error(std::string("cannot convert '") + value + std::string("' into <") + name(T()) + ">") {} }; template struct BadArithmeticExpression : std::runtime_error { static std::string name(int) { return "int"; } static std::string name(bool) { return "bool"; } static std::string name(double) { return "double"; } static std::string name(unsigned int) { return "unsigned int"; } template static std::string name(G) { return std::string(typeid(G).name()); } BadArithmeticExpression(std::string m, std::string value) : std::runtime_error(std::string("cannot evaluate expression '") + value + std::string("' into <") + name(T()) + ">\nParser message: '" + m + "'") {} }; /// trim std::string inline std::string trim(const std::string& oldStr) { std::string swap(oldStr); boost::algorithm::trim(swap); return swap; } /// return the delimiter or throw an exception if there is no known /// delimiter in value inline size_t checkDelim(const std::string& value, const std::string& delims) { size_t pos(std::string::npos); for (unsigned i = 0; i < delims.length(); i++) { pos = value.find(delims[i]); if (pos != std::string::npos) return i; } throw NoDelim("cannot detect the delimiter in " + value); return 0; } /** parse an container from tag tag. The Container must have the properties: * - type value_type * - member function push_back */ template< typename Container > inline void getContainer(const std::string val_, Container& c) { // accepted brackets and delimiters for vector input std::string begBrackets= "{[("; std::string endBrackets= "}])"; std::string delims= ",;"; c.clear(); std::string val = trim(val_); size_t pos = begBrackets.find(val[0]); if (pos == std::string::npos) throw WrongVectorFormat("cannot convert " "'" + val + "' into a list. No leading bracket found!"); if (val[val.length() - 1] != endBrackets[pos]) throw WrongVectorFormat("begin and end bracket are different in" " value '" + val + "'"); size_t oldPos = 1; size_t curDelim = 0; typedef typename Container::value_type ValueType; ValueType swap; try { curDelim = checkDelim(val, delims); pos = val.find(delims[curDelim], oldPos); while (pos != std::string::npos) { std::string curWord = val.substr(oldPos, pos - oldPos); oldPos = pos + 1; convert(curWord, swap); c.push_back(swap); pos = val.find(delims[curDelim], oldPos); } //last entry std::string curWord = val.substr(oldPos, val.length() - 1 - oldPos); convert(curWord, swap); c.push_back(swap); } catch (NoDelim nd) { std::string curWord = val.substr(1, val.length() - 2); curWord = trim(curWord); if (curWord.length() > 0) { // container with one entry convert(curWord, swap); c.push_back(swap); } } } /// convert string to string inline void convert(const std::string valStr, std::string& value) { value = trim(valStr); } /// convert string to intrinsic type template inline void convert(const std::string valStr, T& value , typename boost::enable_if >::type* p = NULL , typename boost::disable_if >::type* p2 = NULL) { using boost::lexical_cast; using boost::numeric_cast; mu::Parser parser; parser.DefineConst(_T("M_PI"), m_pi); parser.DefineConst(_T("M_E"), m_e); try { parser.SetExpr(valStr); value = numeric_cast< T >(parser.Eval()); } catch (boost::bad_lexical_cast e) { throw WrongValueFormat< T >(valStr); } catch (boost::bad_numeric_cast e) { throw WrongValueFormat< T >(valStr); } catch (mu::Parser::exception_type &e) { throw BadArithmeticExpression(e.GetMsg(), valStr); } } template inline void convert(const std::string valStr, T& value, typename boost::enable_if< boost::is_enum< T > >::type* p = NULL) { int swap = 0; try { swap = boost::lexical_cast(trim(valStr)); } catch (boost::bad_lexical_cast e) { throw WrongValueFormat< T >(valStr); } value = static_cast< T >(swap); } /// convert special enums inline void convert(const std::string valStr, Norm& value) { std::string swapStr = boost::to_upper_copy(valStr); if (swapStr == "NO_NORM") value = static_cast< Norm >(NO_NORM); else if (swapStr == "H1_NORM") value = static_cast< Norm >(H1_NORM); else if (swapStr == "L2_NORM") value = static_cast< Norm >(L2_NORM); else { int swap= 0; convert(valStr, swap); value = static_cast< Norm >(swap); } } /// convert string to WorldVector template< typename T > inline void convert(const std::string valStr, WorldVector& c) { std::vector temp_vec; getContainer(valStr, temp_vec); if (static_cast(temp_vec.size()) != c.getSize()) throw WrongVectorSize("wrong number of entries for WorldVector"); for (unsigned i = 0; i < temp_vec.size(); i++) c[i] = temp_vec[i]; } /// convert string to std::list using begBrackets, endBrackets and delims template inline void convert(const std::string valStr, std::list& value) { getContainer(valStr, value); } /// convert string to std::vector using begBrackets, endBrackets and delims template inline void convert(const std::string valStr, std::vector& value) { getContainer(valStr, value); } /// convert value of arbitrary type to string using stringstream and /// operator<< for type template inline void convert(const T value, std::string& valStr) { std::stringstream ss; ss.precision(6); ss << value; valStr = ss.str(); } /// convert WorldVector to string template inline void convert(const WorldVector& c, std::string& valStr) { std::vector temp_vec(c.getSize()); for (unsigned i = 0; i < temp_vec.size(); i++) temp_vec[i] = c[i]; convert(temp_vec, valStr); } } // end namespace InitfileInternal /** The entry in an initfile. This helper class was constructed to allow calls * like val = data.get(tag) for arbitrary types of val. At current stage, only * double and bool is supported */ struct InitEntry { ///the value as string std::string valStr; /// initialize with value as string InitEntry(std::string v = "") : valStr(v) {} /// cast string to type T template operator T() const { T t; convert(valStr, t); return t; } }; /// output-stream for std::list template std::ostream& operator<<(std::ostream& o, const std::list< T >& l) { typename std::list< T >::const_iterator it = l.begin(); o << "["; for (unsigned i = 0; it != l.end() && i < l.size(); i++) { o << *it << (i < l.size() - 1 ? ", " : ""); ++it; } o << "]"; return o; } /// output-stream for std::vector template std::ostream& operator<<(std::ostream& o, const std::vector& l) { typename std::vector::const_iterator it = l.begin(); o << "["; for (unsigned i = 0; it != l.end() && i < l.size(); i++) { o << *it << (i < l.size() - 1 ? ", " : ""); ++it; } o << "]"; return o; } /** Basis data container as a map of tag on a value as strings. The container * throws an exception, if the tag was not found. */ struct Initfile : public std::map { typedef std::map< std::string, std::string > super; static const int TAG_NOT_FOUND = 1; static const int TAG_NOT_FOUND_BREAK = 2; /// Exceptions struct TagNotFound : std::invalid_argument { TagNotFound(std::string m) : std::invalid_argument(m) {} }; struct TagNotFoundBreak : std::invalid_argument { // print 'tag not found' and exit TagNotFoundBreak(std::string m) : std::invalid_argument(m) {} }; /** initialize init-file from file with filename in, read data and save it * to singleton-map * @param in: filename string */ static void init(std::string in); static void init(int print, string filename, const char *flags = NULL) { WARNING("Parameters::init(int,std::string,const char*) is depreciated. " "Use Parameters::init(std::string) instead!\n"); init(filename); } /** Static get routine for getting parameter-values from init-file * initialized in init()-method. * Cast the value to the desired type using std::stringstream. * @param tag: The tag to look for * @param value: The result. * @param debugInfo: msgInfo for current parameter. (0..no printing, * 1..print missing parameter info, 2..print parameter value) [optional] */ template static void get(const std::string tag, T& value, int debugInfo = -1) { using namespace InitfileInternal; initIntern(); if (debugInfo == -1) debugInfo = singlett->getMsgInfo(); std::string valStr; int error_code = singlett->checkedGet(tag, valStr); if (error_code == 0) { valStr = trim(valStr); convert(valStr, value); } else if(error_code == TAG_NOT_FOUND_BREAK) throw TagNotFoundBreak("required tag '" + tag + "' not found"); else if (error_code == TAG_NOT_FOUND && debugInfo == 2) std::cout << "there is no tag '" + tag + "'" << std::endl; if (debugInfo == 2) { std::cout << "Parameter '" << tag << "'" << " initialized with: " << value << std::endl; } } /// return InitEntry object for tag tag static InitEntry get(const std::string tag) { using namespace InitfileInternal; singlett->getMsgInfo(); InitEntry result; std::string valStr; int error_code = singlett->checkedGet(tag, valStr); if (error_code == 0) { valStr = trim(valStr); result = InitEntry(valStr); } else if(error_code == TAG_NOT_FOUND_BREAK) throw TagNotFoundBreak("required tag '" + tag + "' not found"); else if (error_code == TAG_NOT_FOUND) throw TagNotFound("there is no tag '" + tag + "'"); // exception must be thrown, because an empty object would be return otherwise return result; } /// update map tag->value_old to tag->value in singleton template static void set(const std::string tag, T& value, int debugInfo= -1) { using namespace InitfileInternal; initIntern(); if (debugInfo == -1) debugInfo = singlett->getMsgInfo(); std::string swap = ""; convert(value, swap); (*singlett)[trim(tag)] = swap; // update msg parameters msgInfo, msgWait, paramInfo singlett->getInternalParameters(); if (debugInfo == 2) std::cout << "Parameter '" << tag << "'" << " set to: " << value << std::endl; } /// add map tag->value to data in singleton template< typename T > static void add(const std::string tag, T& value, int debugInfo = -1) { set(tag, value, debugInfo); } /// rescheduling parameter static void readArgv(int argc, char **argv); /// Returns specified info level static int getMsgInfo() { return (singlett != NULL) ? singlett->msgInfo : 0; } /// Returns specified wait value static int getMsgWait() { return (singlett != NULL) ? singlett->msgWait : 0; } /// Checks whether parameters are initialized. if not, call init() static bool initialized() { return (singlett != NULL); } /// return pointer to singleton static Initfile *getSingleton() { return singlett; } /// print all data in singleton to std::cout static void printParameters(); /// clear data in singleton static void clearData() { initIntern(); singlett->clear(); } /// save singlett-data to file with filename fn static void save(std::string fn) { initIntern(); singlett->write(fn); } protected: Initfile() : msgInfo(0), msgWait(1), paramInfo(1), breakOnMissingTag(0) {} static void initIntern() { if (singlett == NULL) singlett = new Initfile; } /// list of processed files static std::set< std::string > fn_include_list; /// pointer to the singleton that contains the data static Initfile* singlett; /// return the value of the given tag or throws an exception if the tag /// does not exist int checkedGet(const std::string& tag, std::string &valStr) const { super::const_iterator it = find(tag); if (it == end()) { if (breakOnMissingTag == 0 || msgInfo <= 2) return TAG_NOT_FOUND; else return TAG_NOT_FOUND_BREAK; } valStr = it->second; return 0; } /// replace variables by its value defined as parameter previousely /// variable definition is simple parameter definition /// variable evaluation by ${variablename} or $variablename /// the last version only for variablenames without whitespaces std::string variableReplacement(const std::string& input) const; /** Fill the initfile from an input stream. * @param in: the stream to fill the data from. * Current dataformat: tag:value * Comment char: percent '%' * Include files: #include "filename" or #include */ void read(std::istream& in); /// Fill the initfile from a file with filename fn void read(std::string fn, bool force = false); /** Write data-map to initfile * @param out: the stream to fill the data in. */ void write(std::ostream& out); /// Write data-map to initfile with filename fn void write(std::string fn); /// read parameters for msgInfo, msgWait, paramInfo void getInternalParameters(); int msgInfo, msgWait, paramInfo, breakOnMissingTag; }; typedef Initfile Parameters; } // end namespace AMDiS #endif