#ifndef _theplu_yat_utility_utility_ #define _theplu_yat_utility_utility_ // $Id: utility.h 2943 2013-01-04 06:38:38Z peter $ /* Copyright (C) 2005 Jari Häkkinen, Peter Johansson, Markus Ringnér Copyright (C) 2006 Jari Häkkinen Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson Copyright (C) 2009, 2010, 2011, 2012, 2013 Peter Johansson This file is part of the yat library, http://dev.thep.lu.se/yat The yat library 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. The yat library 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 yat. If not, see . */ /// /// \file yat/utility/utility.h /// /// @brief Some useful functions are placed here /// #include "deprecate.h" #include "Exception.h" #include "yat_assert.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace theplu { namespace yat { namespace utility { /** Same as posix C function with same name but works on \c std::string rather than \c char*. \see http://linux.die.net/man/3/basename \return everything after last '/' \since New in yat 0.10 */ std::string basename(const std::string& fn); /** For each element in resulting range assign it to 0.0 if corresponding element in input range is NaN else assign it to 1.0. \return true if there is at least one NaN in input range [first, last). \since New in yat 0.5 */ template bool binary_weight(InputIterator first, InputIterator last, OutputIterator result); /** Same as C function \c chdir but throws on failure (instead of retrning non-zero). \throw runtime_error if underlying chdir returns non-zero \see http://linux.die.net/man/3/chdir \since New in yat 0.10 */ void chdir(const std::string& dir); /** same as C function \c chmod but throws on failure (instead of returning non-zero). \see http://linux.die.net/man/3/chmod \since New in yat 0.10 */ void chmod(const std::string& filename, mode_t mode); /** \brief convert T to a string T is supposed to be a numerical type. \since new in yat 0.8 */ template std::string convert(T input); /** \brief convert string to (numerical) type \throw runtime_error if conversion fails */ template T convert(const std::string& s); /** @brief Copy file \a source to \a target. @throw runtime_error if read error of \a source or write error for \a target is encountered. */ void copy_file(const std::string& source, const std::string& target); /** Same as posix C function with same name but works on \c std::string rather than \c char*. \see http://linux.die.net/man/3/dirname \return everything prior last '/'. \since New in yat 0.10 */ std::string dirname(const std::string& fn); /** \return true if \a str matches \a pattern \see http://linux.die.net/man/3/fnmatch \throw runtime_error if returned value from underlying fnmatch is neither zero nor \c FNM_NOMATCH. \since New in yat 0.10 */ bool fnmatch(const std::string& pattern, const std::string& str, int flags=0); /** \brief check if string is convertible to (numerical) type \c T \since New in yat 0.5 */ template bool is(const std::string& s); /// /// @return true if string is a double /// /// \deprecated Provided for backward compatibility with the 0.4 /// API. Use is(const std::string&) /// bool is_double(const std::string&) YAT_DEPRECATE; /** This function takes the first word (separated by whitespace) in \a s, replaces all upper case with lower case, and compares it with \a other. \return true if processed \a s is equal to \a other. It returns false otherwise or if \a s contains more than one word. */ bool is_equal(std::string s, std::string other); /// /// @return true if string is a float /// /// \deprecated Provided for backward compatibility with the 0.4 /// API. Use is(const std::string&) /// bool is_float(const std::string&) YAT_DEPRECATE; /// /// @return true if string is an int /// /// \deprecated Provided for backward compatibility with the 0.4 /// API. Use is(const std::string&) /// bool is_int(const std::string&) YAT_DEPRECATE; /// /// @return true if string is "nan" (case-insensitive) /// bool is_nan(const std::string& s); /** The std::istream will be interpreted as outlined here: Lines are separated by character \a line_sep and rows are separated by character \a sep. The first line is read into a stringstream, which is used to load the first vector (vec[0]) with elements using load(stringstream, vec[0], sep). Therefore, column elements separation has two modes depending on the value of \a sep. - If \a sep is the default '\\0' value then column elements are separated with white space characters except the new line character. Multiple sequential white space characters are treated as one separator. - Setting \a sep to something else than the default value will change the behaviour to use the \a sep character as the separator between column elements. Multiple sequential \a sep characters will be treated as separating elements with missing values. If \a rectangle is true, rows must contain same number of elements or function will throw. If \a ignore_empty is true empty lines are ignored. \see load(std::istream&, std::vector&, char sep='\\0') \note Requirement on T: utility::convert must be supported (from yat 0.7 T=string is also supported) \since New in yat 0.6 */ template void load(std::istream& is, std::vector >& vec, char sep='\0', char line_sep='\n', bool ignore_empty=false, bool rectangle=true); /** \brief Fill a vector with elements from istream Element separation has two modes depending on the value of \a sep. - If \a sep is the default '\\0' value then elements are separated with white space characters. Multiple sequential white space characters are treated as one separator. - Setting \a sep to something else than the default value will change the behaviour to use the \a sep character as the separator between column elements. Multiple sequential \a sep characters will be treated as separating elements with missing values. Missing values are set to std::numeric_limits::quiet_NaN \note Requirement on T: utility::convert must be supported (from yat 0.7 T=string is also supported) \since New in yat 0.6 */ template void load(std::istream& is, std::vector& vec, char sep='\0'); /** \return base-2 logarithm of x \since New in yat 0.10 */ // c++11 provides std::log2 so perhaps we should call that one if // availalable (but a bit tricky since this is a public header) template T log2(T x) { return std::log(x)/M_LN2; } // private namespace namespace detail { /** \brief convert s to t used in function is and convert \return true if conversion was successful \internal */ template bool convert(const std::string& s, T& t); /** Functor used in load function */ template struct VectorPusher { /** convert element to T and push on vec's back \internal */ void operator()(const std::string& element, std::vector& vec) { if (!element.size()) vec.push_back(std::numeric_limits::quiet_NaN()); else { vec.push_back(theplu::yat::utility::convert(element)); } } }; /** specialization for string \internal */ template<> struct VectorPusher { /** push element on vec's back */ void operator()(const std::string& element, std::vector& vec) { vec.push_back(element); } }; } // end of namespace detail /** \brief create a directory \a dir \see http://linux.die.net/man/3/mkdir \throw runtime_error if creation failed */ void mkdir(const std::string& dir, mode_t mode=0777); /** Similar to mkdir(const std::string&, mode_t). No error if \a dir already exist. Make parent directories as needed. */ void mkdir_p(const std::string& dir, mode_t mode=0777); /** same as C function remove but throws errno_error at failure \see http://linux.die.net/man/3/remove */ void remove(const std::string& fn); /** same as C function with same name but throws errno_error if error is encountered \see http://linux.die.net/man/3/rename \since New in yat 0.10 */ void rename(const std::string& from, const std::string to); /** In \a full_str replace every sub-string \a old_str with \a new_str; */ void replace(std::string& full_str, std::string old_str, std::string new_str); // template implementations template bool binary_weight(InputIterator first, InputIterator last, OutputIterator result) { bool nan=false; while (first!=last) { if (std::isnan(*first)) { *result=0; nan=true; } else *result = 1.0; ++first; ++result; } return nan; } // template implementations template std::string convert(T input) { std::ostringstream ss; ss << input; return ss.str(); } template T convert(const std::string& s) { T result; if (!detail::convert(s, result)) throw runtime_error(std::string("yat::utility::convert(\"")+s+ std::string("\")")); return result; } template bool is(const std::string& s) { T tmp; return detail::convert(s, tmp); } template void load(std::istream& is, std::vector >& matrix, char sep, char line_sep, bool ignore_empty, bool rectangle) { size_t nof_columns=0; std::string line; while(getline(is, line, line_sep)){ if (line.empty() && ignore_empty) continue; matrix.push_back(std::vector()); std::vector& v=matrix.back(); v.reserve(nof_columns); std::stringstream ss(line); load(ss, v, sep); // add NaN for final separator (or empty string if T=std::string) detail::VectorPusher pusher; if(sep!='\0' && !line.empty() && line[line.size()-1]==sep) pusher("", v); if (rectangle && nof_columns && v.size()!=nof_columns) { std::ostringstream s; s << "load stream error: " << "line " << matrix.size() << " has " << v.size() << " columns; expected " << nof_columns << " columns."; throw utility::IO_error(s.str()); } nof_columns = std::max(nof_columns, v.size()); } // manipulate the state of the stream to be good is.clear(std::ios::goodbit); } template void load(std::istream& is, std::vector& vec, char sep) { detail::VectorPusher pusher; std::string element; bool ok=true; while(true) { if(sep=='\0') ok=(is>>element); else ok=getline(is, element, sep); if(!ok) break; pusher(element, vec); } } namespace detail { template bool convert(const std::string& s, T& result) { if (!std::numeric_limits::is_signed) { // first non-whitespace character std::string::const_iterator iter = s.begin(); while (iter!=s.end() && std::isspace(*iter)) ++iter; // unsigned int cannot start with a '-' and with some compilers // operation ss >> result won't fail so catch it like this instead. if (iter==s.end() || *iter=='-') return false; } std::istringstream ss(s); ss >> result; if (ss.fail()) { if (is_nan(s)) { result = std::numeric_limits::quiet_NaN(); return true; } if (is_equal(s, "inf")) { result = std::numeric_limits::infinity(); return true; } if (is_equal(s, "-inf")) { // unsigned types are caught in prologue YAT_ASSERT(std::numeric_limits::is_signed); result = -std::numeric_limits::infinity(); return true; } return false; } // Check that nothing is left on stream std::string b; ss >> b; return b.empty(); } } // of namespace detail }}} // of namespace utility, yat, and theplu #endif