LCOV - code coverage report
Current view: top level - tools - Tools.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 287 317 90.5 %
Date: 2026-03-30 13:16:06 Functions: 51 55 92.7 %

          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 <dirent.h>
      29             : #include <iostream>
      30             : #include <map>
      31             : #if defined(__PLUMED_HAS_CHDIR) || defined(__PLUMED_HAS_GETCWD)
      32             : #include <unistd.h>
      33             : #endif
      34             : 
      35             : #include <iomanip>
      36             : 
      37             : namespace PLMD {
      38             : 
      39             : template<class T>
      40    17000148 : bool Tools::convertToAny(const std::string & str,T & t) {
      41    31768385 :   std::istringstream istr(str.c_str());
      42    17000148 :   bool ok=static_cast<bool>(istr>>t);
      43    17000148 :   if(!ok) {
      44             :     return false;
      45             :   }
      46             :   std::string remaining;
      47    14896401 :   istr>>remaining;
      48    14896401 :   return remaining.length()==0;
      49    17000148 : }
      50             : 
      51     2228374 : bool Tools::convertNoexcept(const std::string & str,int & t) {
      52     2228374 :   return convertToInt(str,t);
      53             : }
      54             : 
      55           3 : bool Tools::convertNoexcept(const std::string & str,long int & t) {
      56           3 :   return convertToInt(str,t);
      57             : }
      58             : 
      59          87 : bool Tools::convertNoexcept(const std::string & str,long long int & t) {
      60          87 :   return convertToInt(str,t);
      61             : }
      62             : 
      63        5256 : bool Tools::convertNoexcept(const std::string & str,unsigned & t) {
      64        5256 :   return convertToInt(str,t);
      65             : }
      66             : 
      67          97 : bool Tools::convertNoexcept(const std::string & str,long unsigned & t) {
      68          97 :   return convertToInt(str,t);
      69             : }
      70             : 
      71          54 : bool Tools::convertNoexcept(const std::string & str,long long unsigned & t) {
      72          54 :   return convertToInt(str,t);
      73             : }
      74             : 
      75      455798 : bool Tools::convertNoexcept(const std::string & str,AtomNumber &a) {
      76             :   // Note: AtomNumber's are NOT converted as int, so as to
      77             :   // avoid using lepton conversions.
      78             :   unsigned i;
      79      455798 :   bool r=convertToAny(str,i);
      80      455798 :   if(r) {
      81      447412 :     a.setSerial(i);
      82             :   }
      83      455798 :   return r;
      84             : }
      85             : 
      86             : template<class T>
      87     2233871 : bool Tools::convertToInt(const std::string & str,T & t) {
      88             :   // First try standard conversion
      89     2233871 :   if(convertToAny(str,t)) {
      90             :     return true;
      91             :   }
      92             :   // Then use lepton
      93             :   try {
      94     2008031 :     double r=lepton::Parser::parse(str).evaluate(lepton::Constants());
      95             : 
      96             :     // now sanity checks on the resulting number
      97             : 
      98             :     // it should not overflow the requested int type:
      99             :     // (see https://stackoverflow.com/a/526092)
     100     2008031 :     if(r>std::nextafter(std::numeric_limits<T>::max(), 0)) {
     101             :       return false;
     102             :     }
     103     2008031 :     if(r<std::nextafter(std::numeric_limits<T>::min(), 0)) {
     104             :       return false;
     105             :     }
     106             : 
     107             :     // do the actual conversion
     108     2008031 :     auto tmp=static_cast<T>(std::round(r));
     109             : 
     110             :     // it should be *very close* to itself if converted back to double
     111     2008031 :     double diff= r-static_cast<double>(tmp);
     112     2008031 :     if(diff*diff > 1e-20) {
     113             :       return false;
     114             :     }
     115             :     // this is to accomodate small numerical errors and allow e.g. exp(log(7)) to be integer
     116             : 
     117             :     // it should be change if incremented or decremented by one (see https://stackoverflow.com/a/43656140)
     118     2008027 :     if(r == static_cast<double>(tmp-1)) {
     119             :       return false;
     120             :     }
     121     2008026 :     if(r == static_cast<double>(tmp+1)) {
     122             :       return false;
     123             :     }
     124             : 
     125             :     // everything is fine, then store in t
     126     2008026 :     t=tmp;
     127     2008026 :     return true;
     128           0 :   } catch(const PLMD::lepton::Exception& exc) {
     129             :   }
     130           0 :   return false;
     131             : }
     132             : 
     133             : 
     134             : template<class T>
     135    14306942 : bool Tools::convertToReal(const std::string & str,T & t) {
     136    14306942 :   if(convertToAny(str,t)) {
     137             :     return true;
     138             :   }
     139     8378270 :   if(str=="PI" || str=="+PI" || str=="+pi" || str=="pi") {
     140     1047323 :     t=pi;
     141     1047323 :     return true;
     142     2094614 :   } else if(str=="-PI" || str=="-pi") {
     143     1047242 :     t=-pi;
     144     1047242 :     return true;
     145             :   }
     146             :   try {
     147          65 :     t=lepton::Parser::parse(str).evaluate(lepton::Constants());
     148          24 :     return true;
     149          41 :   } catch(const PLMD::lepton::Exception& exc) {
     150             :   }
     151          41 :   if( str.find("PI")!=std::string::npos ) {
     152           0 :     std::size_t pi_start=str.find_first_of("PI");
     153           0 :     if(str.substr(pi_start)!="PI") {
     154             :       return false;
     155             :     }
     156           0 :     std::istringstream nstr(str.substr(0,pi_start));
     157           0 :     T ff=0.0;
     158           0 :     bool ok=static_cast<bool>(nstr>>ff);
     159           0 :     if(!ok) {
     160             :       return false;
     161             :     }
     162           0 :     t=ff*pi;
     163             :     std::string remains;
     164           0 :     nstr>>remains;
     165           0 :     return remains.length()==0;
     166          41 :   } else if( str.find("pi")!=std::string::npos ) {
     167          15 :     std::size_t pi_start=str.find_first_of("pi");
     168          30 :     if(str.substr(pi_start)!="pi") {
     169             :       return false;
     170             :     }
     171          15 :     std::istringstream nstr(str.substr(0,pi_start));
     172          15 :     T ff=0.0;
     173          15 :     bool ok=static_cast<bool>(nstr>>ff);
     174          15 :     if(!ok) {
     175             :       return false;
     176             :     }
     177          15 :     t=ff*pi;
     178             :     std::string remains;
     179          15 :     nstr>>remains;
     180          15 :     return remains.length()==0;
     181          41 :   } else if(str=="NAN") {
     182           0 :     t=std::numeric_limits<double>::quiet_NaN();
     183           0 :     return true;
     184             :   }
     185             :   return false;
     186             : }
     187             : 
     188           0 : bool Tools::convertNoexcept(const std::string & str,float & t) {
     189           0 :   return convertToReal(str,t);
     190             : }
     191             : 
     192    14306814 : bool Tools::convertNoexcept(const std::string & str,double & t) {
     193    14306814 :   return convertToReal(str,t);
     194             : }
     195             : 
     196         128 : bool Tools::convertNoexcept(const std::string & str,long double & t) {
     197         128 :   return convertToReal(str,t);
     198             : }
     199             : 
     200       81337 : bool Tools::convertNoexcept(const std::string & str,std::string & t) {
     201             :   t=str;
     202       81337 :   return true;
     203             : }
     204             : 
     205     3912351 : std::vector<std::string> Tools::getWords(const std::string & line,const char* separators,int * parlevel,const char* parenthesis, const bool& delete_parenthesis) {
     206     3912351 :   plumed_massert(std::strlen(parenthesis)==1,"multiple parenthesis type not available");
     207     3912351 :   plumed_massert(parenthesis[0]=='(' || parenthesis[0]=='[' || parenthesis[0]=='{',
     208             :                  "only ( [ { allowed as parenthesis");
     209     3912351 :   if(!separators) {
     210             :     separators=" \t\n";
     211             :   }
     212     3912351 :   const std::string sep(separators);
     213     3912351 :   char openpar=parenthesis[0];
     214             :   char closepar;
     215             :   if(openpar=='(') {
     216             :     closepar=')';
     217             :   }
     218     3912351 :   if(openpar=='[') {
     219             :     closepar=']';
     220             :   }
     221     3912351 :   if(openpar=='{') {
     222             :     closepar='}';
     223             :   }
     224             :   std::vector<std::string> words;
     225             :   std::string word;
     226             :   int parenthesisLevel=0;
     227     3912351 :   if(parlevel) {
     228       26822 :     parenthesisLevel=*parlevel;
     229             :   }
     230   201059110 :   for(unsigned i=0; i<line.length(); i++) {
     231             :     bool found=false;
     232             :     bool onParenthesis=false;
     233   197146759 :     if( (line[i]==openpar || line[i]==closepar) && delete_parenthesis ) {
     234             :       onParenthesis=true;
     235             :     }
     236   197146759 :     if(line[i]==closepar) {
     237        4569 :       parenthesisLevel--;
     238        4569 :       plumed_massert(parenthesisLevel>=0,"Extra closed parenthesis in '" + line + "'");
     239             :     }
     240   197146759 :     if(parenthesisLevel==0)
     241   788666002 :       for(unsigned j=0; j<sep.length(); j++)
     242   591592385 :         if(line[i]==sep[j]) {
     243             :           found=true;
     244             :         }
     245             : // If at parenthesis level zero (outer)
     246   197146759 :     if(!(parenthesisLevel==0 && (found||onParenthesis))) {
     247   140228247 :       word.push_back(line[i]);
     248             :     }
     249             :     //if(onParenthesis) word.push_back(' ');
     250   197146759 :     if(line[i]==openpar) {
     251        4569 :       parenthesisLevel++;
     252             :     }
     253   197146759 :     if(found && word.length()>0) {
     254    11818787 :       if(!parlevel) {
     255    11773604 :         plumed_massert(parenthesisLevel==0,"Unmatching parenthesis in '" + line + "'");
     256             :       }
     257    11818787 :       words.push_back(word);
     258             :       word.clear();
     259             :     }
     260             :   }
     261     3912351 :   if(word.length()>0) {
     262     3792872 :     if(!parlevel) {
     263     3766296 :       plumed_massert(parenthesisLevel==0,"Unmatching parenthesis in '" + line + "'");
     264             :     }
     265     3792872 :     words.push_back(word);
     266             :   }
     267     3912351 :   if(parlevel) {
     268       26822 :     *parlevel=parenthesisLevel;
     269             :   }
     270     3912351 :   return words;
     271           0 : }
     272             : 
     273       15273 : bool Tools::getParsedLine(IFile& ifile,std::vector<std::string> & words, bool trimcomments) {
     274       15273 :   std::string line("");
     275             :   words.clear();
     276             :   bool stat;
     277             :   bool inside=false;
     278       15273 :   int parlevel=0;
     279             :   bool mergenext=false;
     280       33850 :   while((stat=ifile.getline(line))) {
     281       32953 :     if(trimcomments) {
     282       32953 :       trimComments(line);
     283             :     }
     284       32953 :     trim(line);
     285       32953 :     if(line.length()==0) {
     286        6131 :       continue;
     287             :     }
     288       26822 :     std::vector<std::string> w=getWords(line,NULL,&parlevel,"{",trimcomments);
     289       26822 :     if(!w.empty()) {
     290       38932 :       if(inside && *(w.begin())=="...") {
     291             :         inside=false;
     292        1281 :         if(w.size()==2) {
     293        1126 :           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]);
     294             :         }
     295        1281 :         plumed_massert(w.size()<=2,"terminating \"...\" lines cannot consist of more than two words");
     296             :         w.clear();
     297        1281 :         if(!trimcomments) {
     298           0 :           words.push_back("...");
     299             :         }
     300       25295 :       } else if(*(w.end()-1)=="...") {
     301             :         inside=true;
     302             :         w.erase(w.end()-1);
     303             :       };
     304             :       int i0=0;
     305       26576 :       if(mergenext && words.size()>0 && w.size()>0) {
     306         128 :         words[words.size()-1]+=" "+w[0];
     307             :         i0=1;
     308             :       }
     309       94583 :       for(unsigned i=i0; i<w.size(); ++i) {
     310       68007 :         words.push_back(w[i]);
     311             :       }
     312             :     }
     313       26822 :     mergenext=(parlevel>0);
     314       26822 :     if(!inside) {
     315             :       break;
     316             :     }
     317       12446 :     if(!trimcomments && parlevel==0) {
     318           0 :       words.push_back("@newline");
     319       12446 :     } else if(!trimcomments) {
     320             :       words[words.size()-1] += " @newline";
     321             :     }
     322       26822 :   }
     323       15273 :   plumed_massert(parlevel==0,"non matching parenthesis");
     324       15273 :   if(words.size()>0) {
     325       14220 :     return true;
     326             :   }
     327             :   return stat;
     328             : }
     329             : 
     330             : 
     331     2804049 : bool Tools::getline(FILE* fp,std::string & line) {
     332             :   line="";
     333             :   const int bufferlength=1024;
     334             :   char buffer[bufferlength];
     335             :   bool ret;
     336  2874150225 :   for(int i=0; i<bufferlength; i++) {
     337  2871346176 :     buffer[i]='\0';
     338             :   }
     339     2804049 :   while((ret=fgets(buffer,bufferlength,fp))) {
     340     2803302 :     line.append(buffer);
     341     2803302 :     unsigned ss=std::strlen(buffer);
     342     2803302 :     if(ss>0)
     343     2803302 :       if(buffer[ss-1]=='\n') {
     344             :         break;
     345             :       }
     346             :   };
     347     2804049 :   if(line.length()>0)
     348     2803302 :     if(*(line.end()-1)=='\n') {
     349     2803302 :       line.erase(line.end()-1);
     350             :     }
     351     2804049 :   if(line.length()>0)
     352     2803290 :     if(*(line.end()-1)=='\r') {
     353        1180 :       line.erase(line.end()-1);
     354             :     }
     355     2804049 :   return ret;
     356             : }
     357             : 
     358      996441 : void Tools::trim(std::string & s) {
     359      996441 :   auto n=s.find_last_not_of(" \t");
     360      996441 :   if(n!=std::string::npos) {
     361      983413 :     s.resize(n+1);
     362             :   }
     363      996441 : }
     364             : 
     365     1356982 : void Tools::trimComments(std::string & s) {
     366     1356982 :   auto n=s.find_first_of("#");
     367     1356982 :   if(n!=std::string::npos) {
     368             :     s.resize(n);
     369             :   }
     370     1356982 : }
     371             : 
     372      383090 : bool Tools::caseInSensStringCompare(const std::string & str1, const std::string &str2) {
     373      383090 :   return ((str1.size() == str2.size()) && std::equal(str1.begin(), str1.end(), str2.begin(), [](char c1, char c2) {
     374      712818 :     return (c1 == c2 || std::toupper(c1) == std::toupper(c2));
     375      383090 :   }));
     376             : }
     377             : 
     378      112458 : bool Tools::getKey(std::vector<std::string>& line,const std::string & key,std::string & s,int rep) {
     379             :   s.clear();
     380      437321 :   for(auto p=line.begin(); p!=line.end(); ++p) {
     381      383090 :     if((*p).length()==0) {
     382           0 :       continue;
     383             :     }
     384      383090 :     std::string x=(*p).substr(0,key.length());
     385      383090 :     if(caseInSensStringCompare(x,key)) {
     386       58227 :       if((*p).length()==key.length()) {
     387             :         return false;
     388             :       }
     389       58226 :       std::string tmp=(*p).substr(key.length(),(*p).length());
     390             :       line.erase(p);
     391             :       s=tmp;
     392       58226 :       const std::string multi("@replicas:");
     393       58226 :       if(rep>=0 && startWith(s,multi)) {
     394          24 :         s=s.substr(multi.length(),s.length());
     395          24 :         std::vector<std::string> words=getWords(s,"\t\n ,");
     396          24 :         plumed_massert(rep<static_cast<int>(words.size()),"Number of fields in " + s + " not consistent with number of replicas");
     397          24 :         s=words[rep];
     398          24 :       }
     399             :       return true;
     400             :     }
     401             :   };
     402             :   return false;
     403             : }
     404             : 
     405       11965 : void Tools::interpretRanges(std::vector<std::string>&s) {
     406             :   std::vector<std::string> news;
     407       58979 :   for(const auto & p :s) {
     408       47014 :     news.push_back(p);
     409       47014 :     size_t dash=p.find("-");
     410       47014 :     if(dash==std::string::npos) {
     411       45640 :       continue;
     412             :     }
     413             :     int first;
     414        4254 :     if(!Tools::convertToAny(p.substr(0,dash),first)) {
     415         753 :       continue;
     416             :     }
     417        1374 :     int stride=1;
     418             :     int second;
     419        1374 :     size_t colon=p.substr(dash+1).find(":");
     420        1374 :     if(colon!=std::string::npos) {
     421         108 :       if(!Tools::convertToAny(p.substr(dash+1).substr(0,colon),second) ||
     422         144 :           !Tools::convertToAny(p.substr(dash+1).substr(colon+1),stride)) {
     423           0 :         continue;
     424             :       }
     425             :     } else {
     426        2676 :       if(!Tools::convertToAny(p.substr(dash+1),second)) {
     427           0 :         continue;
     428             :       }
     429             :     }
     430        1374 :     news.resize(news.size()-1);
     431        1374 :     if(first<=second) {
     432        1373 :       plumed_massert(stride>0,"interpreting ranges "+ p + ", stride should be positive");
     433      408015 :       for(int i=first; i<=second; i+=stride) {
     434             :         std::string ss;
     435      406642 :         convert(i,ss);
     436      406642 :         news.push_back(ss);
     437             :       }
     438             :     } else {
     439           1 :       plumed_massert(stride<0,"interpreting ranges "+ p + ", stride should be positive");
     440           3 :       for(int i=first; i>=second; i+=stride) {
     441             :         std::string ss;
     442           2 :         convert(i,ss);
     443           2 :         news.push_back(ss);
     444             :       }
     445             :     }
     446             :   }
     447       11965 :   s=news;
     448       11965 : }
     449             : 
     450       14552 : void Tools::interpretLabel(std::vector<std::string>&s) {
     451       14552 :   if(s.size()<2) {
     452         333 :     return;
     453             :   }
     454       14219 :   std::string s0=s[0];
     455       14219 :   unsigned l=s0.length();
     456       14219 :   if(l<1) {
     457             :     return;
     458             :   }
     459       14219 :   if(s0[l-1]==':') {
     460             :     s[0]=s[1];
     461       21702 :     s[1]="LABEL="+s0.substr(0,l-1);
     462             :   }
     463       14219 :   std::transform(s[0].begin(), s[0].end(), s[0].begin(), ::toupper);
     464             : }
     465             : 
     466        7968 : std::vector<std::string> Tools::ls(const std::string&d) {
     467             :   DIR*dir;
     468             :   std::vector<std::string> result;
     469        7968 :   if ((dir=opendir(d.c_str()))) {
     470             : #if defined(__PLUMED_HAS_READDIR_R)
     471             :     struct dirent ent;
     472             : #endif
     473             :     while(true) {
     474             :       struct dirent *res;
     475             : #if defined(__PLUMED_HAS_READDIR_R)
     476             :       readdir_r(dir,&ent,&res);
     477             : #else
     478      132416 :       res=readdir(dir);
     479             : #endif
     480      132416 :       if(!res) {
     481             :         break;
     482             :       }
     483      489824 :       if(std::string(res->d_name)!="." && std::string(res->d_name)!="..") {
     484      217024 :         result.push_back(res->d_name);
     485             :       }
     486             :     }
     487        7968 :     closedir (dir);
     488             :   }
     489        7968 :   return result;
     490           0 : }
     491             : 
     492        4452 : void Tools::stripLeadingAndTrailingBlanks( std::string& str ) {
     493        4452 :   std::size_t first=str.find_first_not_of(' ');
     494        4452 :   std::size_t last=str.find_last_not_of(' ');
     495        4452 :   if( first<=last && first!=std::string::npos) {
     496        8830 :     str=str.substr(first,last+1);
     497             :   }
     498        4452 : }
     499             : 
     500       12015 : std::string Tools::extension(const std::string&s) {
     501       12015 :   size_t n=s.find_last_of(".");
     502             :   std::string ext;
     503       12015 :   if(n!=std::string::npos && n+1<s.length() && n+5>=s.length()) {
     504        8746 :     ext=s.substr(n+1);
     505        8746 :     if(ext.find("/")!=std::string::npos) {
     506             :       ext="";
     507             :     }
     508        8746 :     std::string base=s.substr(0,n);
     509        8746 :     if(base.length()==0) {
     510             :       ext="";
     511             :     }
     512        8746 :     if(base.length()>0 && base[base.length()-1]=='/') {
     513             :       ext="";
     514             :     }
     515             :   }
     516       12015 :   return ext;
     517             : }
     518             : 
     519          14 : double Tools::bessel0( const double& val ) {
     520          14 :   if (std::abs(val)<3.75) {
     521           2 :     double y = Tools::fastpow( val/3.75, 2 );
     522           2 :     return 1 + y*(3.5156229 +y*(3.0899424 + y*(1.2067492+y*(0.2659732+y*(0.0360768+y*0.0045813)))));
     523             :   }
     524          12 :   double ax=std::abs(val), y=3.75/ax, bx=std::exp(ax)/std::sqrt(ax);
     525          12 :   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)))))));
     526          12 :   return ax*bx;
     527             : }
     528             : 
     529      994566 : bool Tools::startWith(const std::string & full,const std::string &start) {
     530      994566 :   return (full.substr(0,start.length())==start);
     531             : }
     532             : 
     533      103332 : bool Tools::findKeyword(const std::vector<std::string>&line,const std::string&key) {
     534      103332 :   const std::string search(key+"=");
     535      659203 :   for(const auto & p : line) {
     536      605365 :     if(startWith(p,search)) {
     537             :       return true;
     538             :     }
     539             :   }
     540             :   return false;
     541             : }
     542             : 
     543         521 : Tools::DirectoryChanger::DirectoryChanger(const char*path) {
     544         521 :   if(!path) {
     545             :     return;
     546             :   }
     547         521 :   if(std::strlen(path)==0) {
     548             :     return;
     549             :   }
     550             : #ifdef __PLUMED_HAS_GETCWD
     551           2 :   char* ret=getcwd(cwd,buffersize);
     552           2 :   plumed_assert(ret)<<"Name of current directory too long, increase buffer size";
     553             : #else
     554             :   plumed_error()<<"You are trying to use DirectoryChanger but your system does not support getcwd";
     555             : #endif
     556             : #ifdef __PLUMED_HAS_CHDIR
     557           2 :   int r=chdir(path);
     558           3 :   plumed_assert(r==0) <<"Cannot chdir to directory "<<path<<". The directory must exist!";
     559             : #else
     560             :   plumed_error()<<"You are trying to use DirectoryChanger but your system does not support chdir";
     561             : #endif
     562             : }
     563             : 
     564         521 : Tools::DirectoryChanger::~DirectoryChanger() {
     565             : #ifdef __PLUMED_HAS_CHDIR
     566         520 :   if(std::strlen(cwd)==0) {
     567         519 :     return;
     568             :   }
     569           1 :   int ret=chdir(cwd);
     570             : // we cannot put an assertion here (in a destructor) otherwise cppcheck complains
     571             : // we thus just report the problem
     572           1 :   if(ret!=0) {
     573           0 :     std::fprintf(stderr,"+++ WARNING: cannot cd back to directory %s\n",cwd);
     574             :   }
     575             : #endif
     576         520 : }
     577             : 
     578       21147 : std::unique_ptr<std::lock_guard<std::mutex>> Tools::molfile_lock() {
     579             :   static std::mutex mtx;
     580       21147 :   return Tools::make_unique<std::lock_guard<std::mutex>>(mtx);
     581             : }
     582             : 
     583             : /// Internal tool, I am keeping it private for now
     584             : namespace {
     585             : 
     586             : class process_one_exception {
     587             :   std::string & msg;
     588             :   bool first=true;
     589         134 :   void update() {
     590         134 :     if(!first) {
     591          14 :       msg+="\n\nThe above exception was the direct cause of the following exception:\n";
     592             :     }
     593         134 :     first=false;
     594         134 :   }
     595             : public:
     596         120 :   process_one_exception(std::string & msg):
     597         120 :     msg(msg)
     598             :   {}
     599         133 :   void operator()(const std::exception & e) {
     600         133 :     update();
     601         133 :     msg+=e.what();
     602         133 :   }
     603           0 :   void operator()(const std::string & e) {
     604           0 :     update();
     605           0 :     msg+=e;
     606           0 :   }
     607           1 :   void operator()(const char* e) {
     608           1 :     update();
     609           1 :     msg+=e;
     610           1 :   }
     611             : };
     612             : 
     613             : template<class T>
     614         134 : static void process_all_exceptions(T&& f) {
     615             :   try {
     616             :     // First throw the current exception
     617         134 :     throw;
     618         148 :   } catch(const std::nested_exception & e) {
     619             :     // If nested, we go recursive
     620             :     // notice that we apply function f only if exception is also a std::exception
     621             :     try {
     622          14 :       e.rethrow_nested();
     623          28 :     } catch(...) {
     624          14 :       process_all_exceptions(f);
     625             :     }
     626          14 :     auto d=dynamic_cast<const std::exception*>(&e);
     627          14 :     if(d) {
     628          14 :       f(*d);
     629             :     }
     630         238 :   } catch(const std::exception &e) {
     631             :     // If not nested, we end recursion
     632         119 :     f(e);
     633           0 :   } catch(const std::string &e) {
     634             :     // If not nested, we end recursion
     635           0 :     f(e);
     636           2 :   } catch(const char* e) {
     637             :     // If not nested, we end recursion
     638           1 :     f(e);
     639           0 :   } catch(...) {
     640             :     // If not nested and of unknown type, we stop the chain
     641             :   }
     642         134 : }
     643             : 
     644             : }
     645             : 
     646         120 : std::string Tools::concatenateExceptionMessages() {
     647             :   std::string msg;
     648         120 :   process_all_exceptions(process_one_exception(msg));
     649         120 :   return msg;
     650             : }
     651             : 
     652             : }

Generated by: LCOV version 1.16