LCOV - code coverage report
Current view: top level - tools - Tools.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 288 328 87.8 %
Date: 2025-03-25 09:33:27 Functions: 52 57 91.2 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "Tools.h"
      23             : #include "AtomNumber.h"
      24             : #include "Exception.h"
      25             : #include "IFile.h"
      26             : #include "lepton/Lepton.h"
      27             : #include <cstring>
      28             : #include <iostream>
      29             : #include <map>
      30             : #include <iomanip>
      31             : #include <filesystem>
      32             : 
      33             : namespace PLMD {
      34             : 
      35             : template<class T>
      36    22890799 : bool Tools::convertToAny(const std::string & str,T & t) {
      37    43293199 :   std::istringstream istr(str.c_str());
      38    22890799 :   bool ok=static_cast<bool>(istr>>t);
      39    22890799 :   if(!ok) {
      40             :     return false;
      41             :   }
      42             :   std::string remaining;
      43    22848060 :   istr>>remaining;
      44    22848060 :   return remaining.length()==0;
      45    22890799 : }
      46             : 
      47     2482544 : bool Tools::convertNoexcept(const std::string & str,int & t) {
      48     2482544 :   return convertToInt(str,t);
      49             : }
      50             : 
      51           3 : bool Tools::convertNoexcept(const std::string & str,long int & t) {
      52           3 :   return convertToInt(str,t);
      53             : }
      54             : 
      55          87 : bool Tools::convertNoexcept(const std::string & str,long long int & t) {
      56          87 :   return convertToInt(str,t);
      57             : }
      58             : 
      59     4817430 : bool Tools::convertNoexcept(const std::string & str,unsigned & t) {
      60     4817430 :   return convertToInt(str,t);
      61             : }
      62             : 
      63          93 : bool Tools::convertNoexcept(const std::string & str,long unsigned & t) {
      64          93 :   return convertToInt(str,t);
      65             : }
      66             : 
      67          54 : bool Tools::convertNoexcept(const std::string & str,long long unsigned & t) {
      68          54 :   return convertToInt(str,t);
      69             : }
      70             : 
      71     1132455 : bool Tools::convertNoexcept(const std::string & str,AtomNumber &a) {
      72             :   // Note: AtomNumber's are NOT converted as int, so as to
      73             :   // avoid using lepton conversions.
      74             :   unsigned i;
      75     1132455 :   bool r=convertToAny(str,i);
      76     1132455 :   if(r) {
      77     1107674 :     a.setSerial(i);
      78             :   }
      79     1132455 :   return r;
      80             : }
      81             : 
      82             : template<class T>
      83     7300211 : bool Tools::convertToInt(const std::string & str,T & t) {
      84             :   // First try standard conversion
      85     7300211 :   if(convertToAny(str,t)) {
      86             :     return true;
      87             :   }
      88             :   // Then use lepton
      89             :   try {
      90     2008031 :     double r=lepton::Parser::parse(str).evaluate(lepton::Constants());
      91             : 
      92             :     // now sanity checks on the resulting number
      93             : 
      94             :     // it should not overflow the requested int type:
      95             :     // (see https://stackoverflow.com/a/526092)
      96     2008031 :     if(r>std::nextafter(std::numeric_limits<T>::max(), 0)) {
      97             :       return false;
      98             :     }
      99     2008031 :     if(r<std::nextafter(std::numeric_limits<T>::min(), 0)) {
     100             :       return false;
     101             :     }
     102             : 
     103             :     // do the actual conversion
     104     2008031 :     auto tmp=static_cast<T>(std::round(r));
     105             : 
     106             :     // it should be *very close* to itself if converted back to double
     107     2008031 :     double diff= r-static_cast<double>(tmp);
     108     2008031 :     if(diff*diff > 1e-20) {
     109             :       return false;
     110             :     }
     111             :     // this is to accomodate small numerical errors and allow e.g. exp(log(7)) to be integer
     112             : 
     113             :     // it should be change if incremented or decremented by one (see https://stackoverflow.com/a/43656140)
     114     2008027 :     if(r == static_cast<double>(tmp-1)) {
     115             :       return false;
     116             :     }
     117     2008026 :     if(r == static_cast<double>(tmp+1)) {
     118             :       return false;
     119             :     }
     120             : 
     121             :     // everything is fine, then store in t
     122     2008026 :     t=tmp;
     123     2008026 :     return true;
     124           0 :   } catch(const PLMD::lepton::Exception& exc) {
     125             :   }
     126           0 :   return false;
     127             : }
     128             : 
     129             : 
     130             : template<class T>
     131    14452278 : bool Tools::convertToReal(const std::string & str,T & t) {
     132    14452278 :   if(convertToAny(str,t)) {
     133             :     return true;
     134             :   }
     135       68414 :   if(str=="PI" || str=="+PI" || str=="+pi" || str=="pi") {
     136        8597 :     t=pi;
     137        8597 :     return true;
     138       17138 :   } else if(str=="-PI" || str=="-pi") {
     139        8510 :     t=-pi;
     140        8510 :     return true;
     141             :   }
     142             :   try {
     143          59 :     t=lepton::Parser::parse(str).evaluate(lepton::Constants());
     144          24 :     return true;
     145          35 :   } catch(const PLMD::lepton::Exception& exc) {
     146             :   }
     147          35 :   if( str.find("PI")!=std::string::npos ) {
     148           0 :     std::size_t pi_start=str.find_first_of("PI");
     149           0 :     if(str.substr(pi_start)!="PI") {
     150             :       return false;
     151             :     }
     152           0 :     std::istringstream nstr(str.substr(0,pi_start));
     153           0 :     T ff=0.0;
     154           0 :     bool ok=static_cast<bool>(nstr>>ff);
     155           0 :     if(!ok) {
     156             :       return false;
     157             :     }
     158           0 :     t=ff*pi;
     159             :     std::string remains;
     160           0 :     nstr>>remains;
     161           0 :     return remains.length()==0;
     162          35 :   } else if( str.find("pi")!=std::string::npos ) {
     163          13 :     std::size_t pi_start=str.find_first_of("pi");
     164          26 :     if(str.substr(pi_start)!="pi") {
     165             :       return false;
     166             :     }
     167          13 :     std::istringstream nstr(str.substr(0,pi_start));
     168          13 :     T ff=0.0;
     169          13 :     bool ok=static_cast<bool>(nstr>>ff);
     170          13 :     if(!ok) {
     171             :       return false;
     172             :     }
     173          13 :     t=ff*pi;
     174             :     std::string remains;
     175          13 :     nstr>>remains;
     176          13 :     return remains.length()==0;
     177          35 :   } else if(str=="NAN") {
     178           0 :     t=std::numeric_limits<double>::quiet_NaN();
     179           0 :     return true;
     180             :   }
     181             :   return false;
     182             : }
     183             : 
     184           0 : bool Tools::convertNoexcept(const std::string & str,float & t) {
     185           0 :   return convertToReal(str,t);
     186             : }
     187             : 
     188    14451070 : bool Tools::convertNoexcept(const std::string & str,double & t) {
     189    14451070 :   return convertToReal(str,t);
     190             : }
     191             : 
     192        1208 : bool Tools::convertNoexcept(const std::string & str,long double & t) {
     193        1208 :   return convertToReal(str,t);
     194             : }
     195             : 
     196      582648 : bool Tools::convertNoexcept(const std::string & str,std::string & t) {
     197             :   t=str;
     198      582648 :   return true;
     199             : }
     200             : 
     201     3551459 : std::vector<std::string> Tools::getWords(std::string_view line,const char* separators,int * parlevel,const char* parenthesis, const bool& delete_parenthesis) {
     202     3551459 :   plumed_massert(std::strlen(parenthesis)==1,"multiple parenthesis type not available");
     203     3551459 :   plumed_massert(parenthesis[0]=='(' || parenthesis[0]=='[' || parenthesis[0]=='{',
     204             :                  "only ( [ { allowed as parenthesis");
     205     3551459 :   if(!separators) {
     206             :     separators=" \t\n";
     207             :   }
     208     3551459 :   const std::string sep(separators);
     209     3551459 :   char openpar=parenthesis[0];
     210             :   char closepar;
     211             :   if(openpar=='(') {
     212             :     closepar=')';
     213             :   }
     214     3551459 :   if(openpar=='[') {
     215             :     closepar=']';
     216             :   }
     217     3551459 :   if(openpar=='{') {
     218             :     closepar='}';
     219             :   }
     220             :   std::vector<std::string> words;
     221             :   std::string word;
     222             :   int parenthesisLevel=0;
     223     3551459 :   if(parlevel) {
     224       33524 :     parenthesisLevel=*parlevel;
     225             :   }
     226   280822487 :   for(unsigned i=0; i<line.length(); i++) {
     227             :     bool found=false;
     228             :     bool onParenthesis=false;
     229   277271028 :     if( (line[i]==openpar || line[i]==closepar) && delete_parenthesis ) {
     230             :       onParenthesis=true;
     231             :     }
     232   277271028 :     if(line[i]==closepar) {
     233        3745 :       parenthesisLevel--;
     234        3745 :       plumed_assert(parenthesisLevel>=0) << "Extra closed parenthesis in '" << line << "'";
     235             :     }
     236   277271028 :     if(parenthesisLevel==0)
     237  1128889393 :       for(unsigned j=0; j<sep.length(); j++)
     238   851710034 :         if(line[i]==sep[j]) {
     239             :           found=true;
     240             :         }
     241             : // If at parenthesis level zero (outer)
     242   277271028 :     if(!(parenthesisLevel==0 && (found||onParenthesis))) {
     243   209201349 :       word.push_back(line[i]);
     244             :     }
     245             :     //if(onParenthesis) word.push_back(' ');
     246   277271028 :     if(line[i]==openpar) {
     247        3745 :       parenthesisLevel++;
     248             :     }
     249   277271028 :     if(found && word.length()>0) {
     250    18184783 :       if(!parlevel) {
     251    18125866 :         plumed_assert(parenthesisLevel==0) << "Unmatching parenthesis in '" << line << "'";
     252             :       }
     253    18184783 :       words.push_back(word);
     254             :       word.clear();
     255             :     }
     256             :   }
     257     3551459 :   if(word.length()>0) {
     258     3428310 :     if(!parlevel) {
     259     3395076 :       plumed_assert(parenthesisLevel==0) << "Unmatching parenthesis in '" << line << "'";
     260             :     }
     261     3428310 :     words.push_back(word);
     262             :   }
     263     3551459 :   if(parlevel) {
     264       33524 :     *parlevel=parenthesisLevel;
     265             :   }
     266     3551459 :   return words;
     267           0 : }
     268             : 
     269     1368409 : void Tools::getWordsSimple(gch::small_vector<std::string_view> & words,std::string_view line) {
     270             :   words.clear();
     271     1368409 :   auto ptr=line.data();
     272     1368409 :   std::size_t size=0;
     273    14728250 :   for(unsigned i=0; i<line.length(); i++) {
     274    13359841 :     const bool is_separator=(line[i]==' ');
     275    13359841 :     if(!is_separator) {
     276    13317856 :       size++;
     277       41985 :     } else if(size==0) {
     278           0 :       ptr++;
     279             :     } else {
     280             :       words.emplace_back(ptr,size);
     281       41985 :       ptr=&line[i]+1;
     282       41985 :       size=0;
     283             :     }
     284             :   }
     285     1368409 :   if(size>0) {
     286             :     words.emplace_back(ptr,size);
     287             :   }
     288     1368409 : }
     289             : 
     290       19769 : bool Tools::getParsedLine(IFile& ifile,std::vector<std::string> & words, bool trimcomments) {
     291       19769 :   std::string line("");
     292             :   words.clear();
     293             :   bool stat;
     294             :   bool inside=false;
     295       19769 :   int parlevel=0;
     296             :   bool mergenext=false;
     297       41273 :   while((stat=ifile.getline(line))) {
     298       40229 :     if(trimcomments) {
     299       40229 :       trimComments(line);
     300             :     }
     301       40229 :     trim(line);
     302       40229 :     if(line.length()==0) {
     303        6705 :       continue;
     304             :     }
     305       33524 :     std::vector<std::string> w=getWords(line,NULL,&parlevel,"{",trimcomments);
     306       33524 :     if(!w.empty()) {
     307       47915 :       if(inside && *(w.begin())=="...") {
     308             :         inside=false;
     309        1328 :         if(w.size()==2) {
     310        1144 :           plumed_massert(w[1]==words[0],"second word in terminating \"...\" "+w[1]+" line, if present, should be equal to first word of directive: "+words[0]);
     311             :         }
     312        1328 :         plumed_massert(w.size()<=2,"terminating \"...\" lines cannot consist of more than two words");
     313             :         w.clear();
     314        1328 :         if(!trimcomments) {
     315           0 :           words.push_back("...");
     316             :         }
     317       31906 :       } else if(*(w.end()-1)=="...") {
     318             :         inside=true;
     319             :         w.erase(w.end()-1);
     320             :       };
     321             :       int i0=0;
     322       33234 :       if(mergenext && words.size()>0 && w.size()>0) {
     323         128 :         words[words.size()-1]+=" "+w[0];
     324             :         i0=1;
     325             :       }
     326      121521 :       for(unsigned i=i0; i<w.size(); ++i) {
     327       88287 :         words.push_back(w[i]);
     328             :       }
     329             :     }
     330       33524 :     mergenext=(parlevel>0);
     331       33524 :     if(!inside) {
     332             :       break;
     333             :     }
     334       14799 :     if(!trimcomments && parlevel==0) {
     335           0 :       words.push_back("@newline");
     336       14799 :     } else if(!trimcomments) {
     337             :       words[words.size()-1] += " @newline";
     338             :     }
     339       33524 :   }
     340       19769 :   plumed_massert(parlevel==0,"non matching parenthesis");
     341       19769 :   if(words.size()>0) {
     342       18553 :     return true;
     343             :   }
     344             :   return stat;
     345             : }
     346             : 
     347             : 
     348     3348979 : bool Tools::getline(FILE* fp,std::string & line) {
     349             :   line="";
     350             :   const int bufferlength=1024;
     351             :   char buffer[bufferlength];
     352             :   bool ret;
     353  3432703475 :   for(int i=0; i<bufferlength; i++) {
     354  3429354496 :     buffer[i]='\0';
     355             :   }
     356     3348979 :   while((ret=fgets(buffer,bufferlength,fp))) {
     357     3347871 :     line.append(buffer);
     358     3347871 :     unsigned ss=std::strlen(buffer);
     359     3347871 :     if(ss>0)
     360     3347871 :       if(buffer[ss-1]=='\n') {
     361             :         break;
     362             :       }
     363             :   };
     364     3348979 :   if(line.length()>0)
     365     3347871 :     if(*(line.end()-1)=='\n') {
     366     3347871 :       line.erase(line.end()-1);
     367             :     }
     368     3348979 :   if(line.length()>0)
     369     3347859 :     if(*(line.end()-1)=='\r') {
     370        1180 :       line.erase(line.end()-1);
     371             :     }
     372     3348979 :   return ret;
     373             : }
     374             : 
     375     1523260 : void Tools::trim(std::string & s) {
     376     1523260 :   auto n=s.find_last_not_of(" \t");
     377     1523260 :   if(n!=std::string::npos) {
     378     1509490 :     s.resize(n+1);
     379             :   }
     380     1523260 : }
     381             : 
     382        1080 : void Tools::ltrim(std::string & s) {
     383        1080 :   auto n=s.find_first_not_of(" \t");
     384        1080 :   if(n!=std::string::npos) {
     385        2160 :     s = s.substr(n, s.length()-n);
     386             :     s.shrink_to_fit();
     387             :   }
     388        1080 : }
     389             : 
     390     1614632 : void Tools::trimComments(std::string & s) {
     391     1614632 :   auto n=s.find_first_of("#");
     392     1614632 :   if(n!=std::string::npos) {
     393             :     s.resize(n);
     394             :   }
     395     1614632 : }
     396             : 
     397     2781073 : bool Tools::caseInSensStringCompare(const std::string & str1, const std::string &str2) {
     398     2781073 :   return ((str1.size() == str2.size()) && std::equal(str1.begin(), str1.end(), str2.begin(), [](char c1, char c2) {
     399     8152803 :     return (c1 == c2 || std::toupper(c1) == std::toupper(c2));
     400     2781073 :   }));
     401             : }
     402             : 
     403      670236 : bool Tools::getKey(std::vector<std::string>& line,const std::string & key,std::string & s,int rep) {
     404             :   s.clear();
     405     2877844 :   for(auto p=line.begin(); p!=line.end(); ++p) {
     406     2780993 :     if((*p).length()==0) {
     407           0 :       continue;
     408             :     }
     409     2780993 :     std::string x=(*p).substr(0,key.length());
     410     2780993 :     if(caseInSensStringCompare(x,key)) {
     411      573385 :       if((*p).length()==key.length()) {
     412             :         return false;
     413             :       }
     414      573384 :       std::string tmp=(*p).substr(key.length(),(*p).length());
     415             :       line.erase(p);
     416             :       s=tmp;
     417      573384 :       const std::string multi("@replicas:");
     418      573384 :       if(rep>=0 && startWith(s,multi)) {
     419          24 :         s=s.substr(multi.length(),s.length());
     420          24 :         std::vector<std::string> words=getWords(s,"\t\n ,");
     421          24 :         plumed_massert(rep<static_cast<int>(words.size()),"Number of fields in " + s + " not consistent with number of replicas");
     422          24 :         s=words[rep];
     423          24 :       }
     424             :       return true;
     425             :     }
     426             :   };
     427             :   return false;
     428             : }
     429             : 
     430       48479 : void Tools::interpretRanges(std::vector<std::string>&s) {
     431             :   std::vector<std::string> news;
     432      622865 :   for(const auto & p :s) {
     433      574386 :     news.push_back(p);
     434      574386 :     size_t dash=p.find("-");
     435      574386 :     if(dash==std::string::npos) {
     436      571897 :       continue;
     437             :     }
     438             :     int first;
     439        6602 :     if(!Tools::convertToAny(p.substr(0,dash),first)) {
     440         812 :       continue;
     441             :     }
     442        2489 :     int stride=1;
     443             :     int second;
     444        2489 :     size_t colon=p.substr(dash+1).find(":");
     445        2489 :     if(colon!=std::string::npos) {
     446         195 :       if(!Tools::convertToAny(p.substr(dash+1).substr(0,colon),second) ||
     447         260 :           !Tools::convertToAny(p.substr(dash+1).substr(colon+1),stride)) {
     448           0 :         continue;
     449             :       }
     450             :     } else {
     451        4848 :       if(!Tools::convertToAny(p.substr(dash+1),second)) {
     452           0 :         continue;
     453             :       }
     454             :     }
     455        2489 :     news.resize(news.size()-1);
     456        2489 :     if(first<=second) {
     457        2488 :       plumed_massert(stride>0,"interpreting ranges "+ p + ", stride should be positive");
     458      568663 :       for(int i=first; i<=second; i+=stride) {
     459             :         std::string ss;
     460      566175 :         convert(i,ss);
     461      566175 :         news.push_back(ss);
     462             :       }
     463             :     } else {
     464           1 :       plumed_massert(stride<0,"interpreting ranges "+ p + ", stride should be positive");
     465           3 :       for(int i=first; i>=second; i+=stride) {
     466             :         std::string ss;
     467           2 :         convert(i,ss);
     468           2 :         news.push_back(ss);
     469             :       }
     470             :     }
     471             :   }
     472       48479 :   s=news;
     473       48479 : }
     474             : 
     475       72120 : void Tools::interpretLabel(std::vector<std::string>&s) {
     476       72120 :   if(s.size()<2) {
     477         352 :     return;
     478             :   }
     479       71768 :   std::string s0=s[0];
     480       71768 :   unsigned l=s0.length();
     481       71768 :   if(l<1) {
     482             :     return;
     483             :   }
     484       71768 :   if(s0[l-1]==':') {
     485             :     s[0]=s[1];
     486       91970 :     s[1]="LABEL="+s0.substr(0,l-1);
     487             :   }
     488       71768 :   std::transform(s[0].begin(), s[0].end(), s[0].begin(), ::toupper);
     489             : }
     490             : 
     491        9308 : std::vector<std::string> Tools::ls(const std::string&d) {
     492             :   std::vector<std::string> result;
     493      150212 :   for (auto const& dir_entry : std::filesystem::directory_iterator{d}) {
     494      366864 :     result.push_back(dir_entry.path().filename());
     495             :   }
     496        9308 :   return result;
     497           0 : }
     498             : 
     499        4488 : void Tools::stripLeadingAndTrailingBlanks( std::string& str ) {
     500        4488 :   std::size_t first=str.find_first_not_of(' ');
     501        4488 :   std::size_t last=str.find_last_not_of(' ');
     502        4488 :   if( first<=last && first!=std::string::npos) {
     503        8902 :     str=str.substr(first,last+1);
     504             :   }
     505        4488 : }
     506             : 
     507       13350 : std::string Tools::extension(const std::string&s) {
     508       13350 :   size_t n=s.find_last_of(".");
     509             :   std::string ext;
     510       13350 :   if(n!=std::string::npos && n+1<s.length() && n+5>=s.length()) {
     511        9399 :     ext=s.substr(n+1);
     512        9399 :     if(ext.find("/")!=std::string::npos) {
     513             :       ext="";
     514             :     }
     515        9399 :     std::string base=s.substr(0,n);
     516        9399 :     if(base.length()==0) {
     517             :       ext="";
     518             :     }
     519        9399 :     if(base.length()>0 && base[base.length()-1]=='/') {
     520             :       ext="";
     521             :     }
     522             :   }
     523       13350 :   return ext;
     524             : }
     525             : 
     526           0 : double Tools::bessel0( const double& val ) {
     527           0 :   if (std::abs(val)<3.75) {
     528           0 :     double y = Tools::fastpow( val/3.75, 2 );
     529           0 :     return 1 + y*(3.5156229 +y*(3.0899424 + y*(1.2067492+y*(0.2659732+y*(0.0360768+y*0.0045813)))));
     530             :   }
     531           0 :   double ax=std::abs(val), y=3.75/ax, bx=std::exp(ax)/std::sqrt(ax);
     532           0 :   ax=0.39894228+y*(0.01328592+y*(0.00225319+y*(-0.00157565+y*(0.00916281+y*(-0.02057706+y*(0.02635537+y*(-0.01647633+y*0.00392377)))))));
     533           0 :   return ax*bx;
     534             : }
     535             : 
     536   515064163 : bool Tools::startWith(const std::string & full,const std::string &start) {
     537   515064163 :   return (full.substr(0,start.length())==start);
     538             : }
     539             : 
     540      425587 : bool Tools::findKeyword(const std::vector<std::string>&line,const std::string&key) {
     541      425587 :   const std::string search(key+"=");
     542   509388501 :   for(const auto & p : line) {
     543   509112392 :     if(startWith(p,search)) {
     544             :       return true;
     545             :     }
     546             :   }
     547             :   return false;
     548             : }
     549             : 
     550         521 : Tools::DirectoryChanger::DirectoryChanger(const char*path):
     551         521 :   path(std::filesystem::current_path()) {
     552         521 :   if(!path) {
     553             :     return;
     554             :   }
     555         521 :   if(std::strlen(path)==0) {
     556             :     return;
     557             :   }
     558           4 :   std::filesystem::current_path(path);
     559             : }
     560             : 
     561         520 : Tools::DirectoryChanger::~DirectoryChanger() {
     562             :   try {
     563         520 :     std::filesystem::current_path(path);
     564           0 :   } catch(std::filesystem::filesystem_error & e) {
     565           0 :     std::fprintf(stderr,"+++ WARNING: cannot cd back to directory %s\n",path.c_str());
     566           0 :   }
     567         520 : }
     568             : 
     569       21273 : std::unique_ptr<std::lock_guard<std::mutex>> Tools::molfile_lock() {
     570             :   static std::mutex mtx;
     571       21273 :   return Tools::make_unique<std::lock_guard<std::mutex>>(mtx);
     572             : }
     573             : 
     574             : /// Internal tool, I am keeping it private for now
     575             : namespace {
     576             : 
     577             : class process_one_exception {
     578             :   std::string & msg;
     579             :   bool first=true;
     580         216 :   void update() {
     581         216 :     if(!first) {
     582          14 :       msg+="\n\nThe above exception was the direct cause of the following exception:\n";
     583             :     }
     584         216 :     first=false;
     585         216 :   }
     586             : public:
     587         202 :   process_one_exception(std::string & msg):
     588         202 :     msg(msg)
     589             :   {}
     590         215 :   void operator()(const std::exception & e) {
     591         215 :     update();
     592         215 :     msg+=e.what();
     593         215 :   }
     594           0 :   void operator()(const std::string & e) {
     595           0 :     update();
     596           0 :     msg+=e;
     597           0 :   }
     598           1 :   void operator()(const char* e) {
     599           1 :     update();
     600           1 :     msg+=e;
     601           1 :   }
     602             : };
     603             : 
     604             : template<class T>
     605         216 : static void process_all_exceptions(T&& f) {
     606             :   try {
     607             :     // First throw the current exception
     608         216 :     throw;
     609         230 :   } catch(const std::nested_exception & e) {
     610             :     // If nested, we go recursive
     611             :     // notice that we apply function f only if exception is also a std::exception
     612             :     try {
     613          14 :       e.rethrow_nested();
     614          28 :     } catch(...) {
     615          14 :       process_all_exceptions(f);
     616             :     }
     617          14 :     auto d=dynamic_cast<const std::exception*>(&e);
     618          14 :     if(d) {
     619          14 :       f(*d);
     620             :     }
     621         402 :   } catch(const std::exception &e) {
     622             :     // If not nested, we end recursion
     623         201 :     f(e);
     624           0 :   } catch(const std::string &e) {
     625             :     // If not nested, we end recursion
     626           0 :     f(e);
     627           2 :   } catch(const char* e) {
     628             :     // If not nested, we end recursion
     629           1 :     f(e);
     630           0 :   } catch(...) {
     631             :     // If not nested and of unknown type, we stop the chain
     632             :   }
     633         216 : }
     634             : 
     635             : }
     636             : 
     637         202 : std::string Tools::concatenateExceptionMessages() {
     638             :   std::string msg;
     639         202 :   process_all_exceptions(process_one_exception(msg));
     640         202 :   return msg;
     641             : }
     642             : 
     643             : }

Generated by: LCOV version 1.16