// -*- C++ -*- // // This file is part of LHAPDF // Copyright (C) 2012-2021 The LHAPDF collaboration (see AUTHORS for details) // #pragma once #ifndef LHAPDF_Utils_H #define LHAPDF_Utils_H // STL includes #include #include #include #include #include #include #include #include #include #include #include #include /// Namespace for all LHAPDF functions and classes namespace LHAPDF { // Allow implicit use of the std namespace within namespace LHAPDF using namespace std; /// @defgroup utils Internal utility functions ///@{ /// @name String handling utility functions ///@{ /// When lexical_cast goes bad struct bad_lexical_cast : public std::runtime_error { /// Constructor bad_lexical_cast(const std::string& what) : std::runtime_error(what) {} }; /// @brief Convert between types via stringstream template T lexical_cast(const U& in) { try { std::stringstream ss; ss << in; T out; ss >> out; return out; } catch (const std::exception& e) { throw bad_lexical_cast(e.what()); } } /// Make a string representation of @a val template inline std::string to_str(const T& val) { return lexical_cast(val); } /// Make a string representation of a vector @a vec template inline std::string to_str(const std::vector& vec) { string rtn = "["; for (size_t i = 0; i < vec.size(); ++i) { rtn += to_str(vec[i]); if (i < vec.size()-1) rtn += ", "; } rtn += "]"; return rtn; } /// Format an integer @a val as a zero-padded string of length @a nchars inline std::string to_str_zeropad(int val, size_t nchars=4) { stringstream ss; ss << setfill('0') << setw(nchars) << val; return ss.str(); } /// Concatenate strings with separator strings between each element inline std::string join(const std::vector& svec, const std::string& sep) { string rtn; for (size_t i = 0; i < svec.size(); ++i) { rtn += svec[i]; if (i < svec.size()-1) rtn += sep; } return rtn; } /// Split a string by a given separator inline std::vector split(const std::string& s, const std::string& sep) { vector rtn; string tmp = s; // non-const working copy, to be incrementally truncated while (true) { const size_t delim_pos = tmp.find(sep); if (delim_pos == string::npos) break; const string s = tmp.substr(0, delim_pos); if (!s.empty()) rtn.push_back(s); // Don't insert "empties" tmp.replace(0, delim_pos+1, ""); // Remove already-processed part } if (!tmp.empty()) rtn.push_back(tmp); // Don't forget the trailing component! return rtn; } /// Does a string @a s contain the @a sub substring? inline bool contains(const std::string& s, const std::string& sub) { return s.find(sub) != string::npos; } /// Does a string @a s start with the @a sub substring? inline bool startswith(const std::string& s, const std::string& sub) { return s.find(sub) == 0; } /// Does a string @a s end with the @a sub substring? inline bool endswith(const std::string& s, const std::string& sub) { return s.find(sub) == s.length()-sub.length(); } /// How many times does a string @a s contain the character @a c? inline size_t countchar(const std::string& s, const char c) { return std::count(s.begin(), s.end(), c); } /// Strip leading and trailing spaces (not in-place) inline std::string trim(const std::string& s) { const size_t firstnonspacepos = s.find_first_not_of(" "); const size_t lastnonspacepos = s.find_last_not_of(" "); if (firstnonspacepos == std::string::npos) return ""; return s.substr(firstnonspacepos, lastnonspacepos-firstnonspacepos+1); } /// Convert a string to lower-case (not in-place) inline std::string to_lower(const std::string& s) { string rtn = s; transform(rtn.begin(), rtn.end(), rtn.begin(), (int(*)(int)) tolower); return rtn; } /// Convert a string to upper-case (not in-place) inline std::string to_upper(const std::string& s) { string rtn = s; transform(rtn.begin(), rtn.end(), rtn.begin(), (int(*)(int)) toupper); return rtn; } ///@} /// @name Filesystem utils ///@{ /// Check if a path @a p (either file or dir) exists bool path_exists(const std::string& p,int mode=0); /// Check if a file @a p exists bool file_exists(const std::string& p,int mode=0); /// Check if a dir @a p exists bool dir_exists(const std::string& p,int mode=0); /// Operator for joining strings @a a and @a b with filesystem separators inline std::string operator / (const std::string& a, const std::string& b) { // Ensure that a doesn't end with a slash, and b doesn't start with one, to avoid "//" const string anorm = (a.find("/") != std::string::npos) ? a.substr(0, a.find_last_not_of("/")+1) : a; const string bnorm = (b.find("/") != std::string::npos) ? b.substr(b.find_first_not_of("/")) : b; return anorm + "/" + bnorm; } /// Get the basename (i.e. terminal file name) from a path @a p inline std::string basename(const std::string& p) { if (!contains(p, "/")) return p; return p.substr(p.rfind("/")+1); } /// Get the dirname (i.e. path to the penultimate directory) from a path @a p inline std::string dirname(const std::string& p) { if (!contains(p, "/")) return ""; return p.substr(0, p.rfind("/")); } /// Get the stem (i.e. part without a file extension) from a filename @a f inline std::string file_stem(const std::string& f) { if (!contains(f, ".")) return f; return f.substr(0, f.rfind(".")); } /// Get the file extension from a filename @a f inline std::string file_extn(const std::string& f) { if (!contains(f, ".")) return ""; return f.substr(f.rfind(".")+1); } /// @todo Add an abspath(p) function ///@} /// @name Math functions ///@{ /// Convenience function for squaring (of any type) template inline N sqr(const N& x) { return x*x; } /// Get the sign of a number template inline int sgn(N val) { return (N(0) < val) - (val < N(0)); } /// Check if a number is in a range (closed-open) inline int in_range(double x, double low, double high) { return x >= low && x < high; } /// Check if a number is in a range (closed-closed) inline int in_closed_range(double x, double low, double high) { return x >= low && x <= high; } /// Check if a number is in a range (open-open) inline int in_open_range(double x, double low, double high) { return x > low && x < high; } /// @todo Add iszero() & equals(,) functions? /// Quantiles of the standard normal probability distribution function double norm_quantile(double p); /// Quantiles of the chi-squared probability distribution function double chisquared_quantile(double p, double ndf); ///@} /// @name Container utils ///@{ /// Does the vector @a container contain @a item? template inline bool contains(const std::vector& container, const T& item) { return find(container.begin(), container.end(), item) != container.end(); } // /// Does the set @a container contain @a item? // template // inline bool contains(const std::set& container, const T& item) { // return container.find(item) != container.end(); // } /// Does the map @a container have a key K @a key? template inline bool has_key(const std::map& container, const K& key) { return container.find(key) != container.end(); } // /// @name Implementation of generic begin/end container identification by traits // /// taken from http://stackoverflow.com/a/9407420/91808 . Needs C++11 (or maybe just C++0x). // //@{ // #include // template // struct has_const_iterator { // private: // typedef char yes; // typedef struct { char array[2]; } no; // template static yes test(typename C::const_iterator*); // template static no test(...); // public: // static const bool value = sizeof(test(0)) == sizeof(yes); // typedef T type; // }; // template // struct has_begin_end { // template static char (&f(typename std::enable_if< // std::is_same(&C::begin)), // typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; // template static char (&f(...))[2]; // template static char (&g(typename std::enable_if< // std::is_same(&C::end)), // typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; // template static char (&g(...))[2]; // static bool const beg_value = sizeof(f(0)) == 1; // static bool const end_value = sizeof(g(0)) == 1; // }; // template // struct is_container // : std::integral_constant::value && // has_begin_end::beg_value && has_begin_end::end_value> // { }; ///@} } #endif