From b072a481baec419e4760ea420c27ec0a6e2f75b3 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 11:15:02 +0200 Subject: [PATCH 01/24] PMsrHandler: replace ROOT tokenizer machinery with C++17 PStringUtils Reduce the ROOT footprint of the MSR parser by removing the pervasive TString::Tokenize / TObjArray / TObjString / dynamic_cast pattern (28 tokenize sites, 14 TObjArray, 106 TObjString) used to split lines into tokens, together with the manual `delete tokens` cleanup. Add a new dependency-free C++17 utility class PStringUtils (Split, IsInt, IsFloat, ToInt, ToDouble, IsEqualNoCase, ContainsNoCase, BeginsWithNoCase) that replicates the relevant TString semantics exactly, so it can be reused elsewhere in the suite. IsInt/IsFloat tolerate surrounding whitespace to match TString::IsDigit/IsFloat (needed for tokens split on ',' / ';' only). The public API and the PMusr.h data structures keep TString unchanged; only the internal tokenizing logic is rewritten. Net -451 lines in PMsrHandler.cpp. All 85 integration tests pass. Co-Authored-By: Claude Opus 4.8 --- src/classes/CMakeLists.txt | 2 + src/classes/PMsrHandler.cpp | 1201 +++++++++++----------------------- src/classes/PStringUtils.cpp | 225 +++++++ src/include/PStringUtils.h | 137 ++++ 4 files changed, 738 insertions(+), 827 deletions(-) create mode 100644 src/classes/PStringUtils.cpp create mode 100644 src/include/PStringUtils.h diff --git a/src/classes/CMakeLists.txt b/src/classes/CMakeLists.txt index 5ade0bb8..3ddbfd3b 100644 --- a/src/classes/CMakeLists.txt +++ b/src/classes/CMakeLists.txt @@ -106,6 +106,7 @@ add_library(PMusr SHARED PMsgBoxDict.cxx PMsr2Data.cpp PMsrHandler.cpp + PStringUtils.cpp PMusrCanvas.cpp PMusrCanvasDict.cxx PMusr.cpp @@ -270,6 +271,7 @@ install( ${MUSRFIT_INC}/PRunSingleHisto.h ${MUSRFIT_INC}/PRunSingleHistoRRF.h ${MUSRFIT_INC}/PStartupHandler.h + ${MUSRFIT_INC}/PStringUtils.h ${MUSRFIT_INC}/PTheory.h ${MUSRFIT_INC}/PUserFcnBase.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} diff --git a/src/classes/PMsrHandler.cpp b/src/classes/PMsrHandler.cpp index 8194546b..7db1597e 100644 --- a/src/classes/PMsrHandler.cpp +++ b/src/classes/PMsrHandler.cpp @@ -30,16 +30,16 @@ #include #include +#include #include #include #include -#include -#include #include #include "PMusr.h" #include "PMsrHandler.h" +#include "PStringUtils.h" //-------------------------------------------------------------------------- // Constructor @@ -451,8 +451,6 @@ Int_t PMsrHandler::WriteMsrLogFile(const Bool_t messages) Int_t plotNo = -1; std::string line; TString logFileName, str, sstr, *pstr; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; Bool_t found = false; Bool_t statisticBlockFound = false; Bool_t partialStatisticBlockFound = true; @@ -593,15 +591,13 @@ Int_t PMsrHandler::WriteMsrLogFile(const Bool_t messages) else fout << str.Data() << std::endl; break; - case MSR_TAG_FITPARAMETER: - tokens = str.Tokenize(" \t"); - if (tokens->GetEntries() == 0) { // not a parameter line + case MSR_TAG_FITPARAMETER: { + std::vector tokens = PStringUtils::Split(str.Data(), " \t"); + if (tokens.empty()) { // not a parameter line fout << str.Data() << std::endl; } else { - ostr = dynamic_cast(tokens->At(0)); - sstr = ostr->GetString(); - if (sstr.IsDigit()) { // parameter - number = sstr.Atoi(); + if (PStringUtils::IsInt(tokens[0])) { // parameter + number = PStringUtils::ToInt(tokens[0]); number--; // make sure number makes sense assert ((number >= 0) && (number < (Int_t)fParam.size())); @@ -670,10 +666,9 @@ Int_t PMsrHandler::WriteMsrLogFile(const Bool_t messages) } else { // not a parameter, hence just copy it fout << str.Data() << std::endl; } - // clean up tokens - delete tokens; } break; + } case MSR_TAG_THEORY: found = false; for (UInt_t i=0; i tokens; // fill param structure iter = lines.begin(); @@ -2865,66 +2858,48 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) param.fUpperBoundaryPresent = false; param.fUpperBoundary = 0.0; - tokens = iter->fLine.Tokenize(" \t"); - if (!tokens) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::HandleFitParameterEntry: **SEVERE ERROR** Couldn't tokenize Parameters in line " << iter->fLineNo << "\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } + tokens = PStringUtils::Split(iter->fLine.Data(), " \t"); // handle various input possiblities - if ((tokens->GetEntries() < 4) || (tokens->GetEntries() > 7) || (tokens->GetEntries() == 6)) { + if ((tokens.size() < 4) || (tokens.size() > 7) || (tokens.size() == 6)) { error = true; } else { // handle the first 4 parameter since they are always the same // parameter number - ostr = dynamic_cast(tokens->At(0)); - str = ostr->GetString(); - if (str.IsDigit()) - param.fNo = str.Atoi(); + if (PStringUtils::IsInt(tokens[0])) + param.fNo = PStringUtils::ToInt(tokens[0]); else error = true; // parameter name - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - param.fName = str; + param.fName = tokens[1].c_str(); // parameter value - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fValue = static_cast(str.Atof()); + if (PStringUtils::IsFloat(tokens[2])) + param.fValue = PStringUtils::ToDouble(tokens[2]); else error = true; // parameter value - ostr = dynamic_cast(tokens->At(3)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fStep = static_cast(str.Atof()); + if (PStringUtils::IsFloat(tokens[3])) + param.fStep = PStringUtils::ToDouble(tokens[3]); else error = true; // 4 values, i.e. No Name Value Step - if (tokens->GetEntries() == 4) { + if (tokens.size() == 4) { param.fNoOfParams = 4; } // 5 values, i.e. No Name Value Neg_Error Pos_Error - if (tokens->GetEntries() == 5) { + if (tokens.size() == 5) { param.fNoOfParams = 5; // positive error - ostr = dynamic_cast(tokens->At(4)); - str = ostr->GetString(); - if (str.IsFloat()) { + if (PStringUtils::IsFloat(tokens[4])) { param.fPosErrorPresent = true; - param.fPosError = static_cast(str.Atof()); + param.fPosError = PStringUtils::ToDouble(tokens[4]); } else { - str.ToLower(); - if (!str.CompareTo("none", TString::kIgnoreCase)) + if (PStringUtils::IsEqualNoCase(tokens[4], "none")) param.fPosErrorPresent = false; else error = true; @@ -2932,32 +2907,27 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) } // 7 values, i.e. No Name Value Neg_Error Pos_Error Lower_Boundary Upper_Boundary - if (tokens->GetEntries() == 7) { + if (tokens.size() == 7) { param.fNoOfParams = 7; // positive error - ostr = dynamic_cast(tokens->At(4)); - str = ostr->GetString(); - if (str.IsFloat()) { + if (PStringUtils::IsFloat(tokens[4])) { param.fPosErrorPresent = true; - param.fPosError = static_cast(str.Atof()); + param.fPosError = PStringUtils::ToDouble(tokens[4]); } else { - str.ToLower(); - if (!str.CompareTo("none", TString::kIgnoreCase)) + if (PStringUtils::IsEqualNoCase(tokens[4], "none")) param.fPosErrorPresent = false; else error = true; } // lower boundary - ostr = dynamic_cast(tokens->At(5)); - str = ostr->GetString(); // check if lower boundary is "none", i.e. upper boundary limited only - if (!str.CompareTo("none", TString::kIgnoreCase)) { // none + if (PStringUtils::IsEqualNoCase(tokens[5], "none")) { // none param.fLowerBoundaryPresent = false; } else { // assuming that the lower boundary is a number - if (str.IsFloat()) { - param.fLowerBoundary = static_cast(str.Atof()); + if (PStringUtils::IsFloat(tokens[5])) { + param.fLowerBoundary = PStringUtils::ToDouble(tokens[5]); param.fLowerBoundaryPresent = true; } else { error = true; @@ -2965,14 +2935,12 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) } // upper boundary - ostr = dynamic_cast(tokens->At(6)); - str = ostr->GetString(); // check if upper boundary is "none", i.e. lower boundary limited only - if (!str.CompareTo("none", TString::kIgnoreCase)) { // none + if (PStringUtils::IsEqualNoCase(tokens[6], "none")) { // none param.fUpperBoundaryPresent = false; } else { // assuming a number - if (str.IsFloat()) { - param.fUpperBoundary = static_cast(str.Atof()); + if (PStringUtils::IsFloat(tokens[6])) { + param.fUpperBoundary = PStringUtils::ToDouble(tokens[6]); param.fUpperBoundaryPresent = true; } else { error = true; @@ -3012,12 +2980,6 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) fParam.push_back(param); } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } - iter++; } @@ -3122,8 +3084,7 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) Bool_t error = false; TString str; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; + std::vector tokens; Int_t ival = 0; Double_t dval = 0.0; UInt_t addT0Counter = 0; @@ -3140,23 +3101,14 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) str.Remove(idx); // tokenize line - tokens = str.Tokenize(" \t"); - if (!tokens) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::HandleGlobalEntry: **SEVERE ERROR** Couldn't tokenize line " << iter->fLineNo << "\n\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } + tokens = PStringUtils::Split(str.Data(), " \t"); if (iter->fLine.BeginsWith("fittype", TString::kIgnoreCase)) { // fittype - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - Int_t fittype = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + Int_t fittype = PStringUtils::ToInt(tokens[1]); if ((fittype == MSR_FITTYPE_SINGLE_HISTO) || (fittype == MSR_FITTYPE_SINGLE_HISTO_RRF) || (fittype == MSR_FITTYPE_ASYM) || @@ -3173,32 +3125,26 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("rrf_freq", TString::kIgnoreCase)) { - if (tokens->GetEntries() < 3) { + if (tokens.size() < 3) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsFloat()) { - dval = str.Atof(); + if (PStringUtils::IsFloat(tokens[1])) { + dval = PStringUtils::ToDouble(tokens[1]); if (dval <= 0.0) error = true; } if (!error) { - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - global.SetRRFFreq(dval, str.Data()); - if (global.GetRRFFreq(str.Data()) == RRF_FREQ_UNDEF) + global.SetRRFFreq(dval, tokens[2].c_str()); + if (global.GetRRFFreq(tokens[2].c_str()) == RRF_FREQ_UNDEF) error = true; } } } else if (iter->fLine.BeginsWith("rrf_packing", TString::kIgnoreCase)) { - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival > 0) { global.SetRRFPacking(ival); } else { @@ -3209,27 +3155,23 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("rrf_phase", TString::kIgnoreCase)) { - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsFloat()) { - dval = str.Atof(); + if (PStringUtils::IsFloat(tokens[1])) { + dval = PStringUtils::ToDouble(tokens[1]); global.SetRRFPhase(dval); } else { error = true; } } } else if (iter->fLine.BeginsWith("data", TString::kIgnoreCase)) { // data - if (tokens->GetEntries() < 3) { + if (tokens.size() < 3) { error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + for (UInt_t i=1; i= 0) { global.SetDataRange(ival, i-1); } else { @@ -3241,14 +3183,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("t0", TString::kIgnoreCase)) { // t0 - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) { - dval = str.Atof(); + for (UInt_t i=1; i= 0.0) global.SetT0Bin(dval); else @@ -3259,14 +3199,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("addt0", TString::kIgnoreCase)) { // addt0 - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) { - dval = str.Atof(); + for (UInt_t i=1; i= 0.0) global.SetAddT0Bin(dval, addT0Counter, i-1); else @@ -3278,19 +3216,17 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) } addT0Counter++; } else if (iter->fLine.BeginsWith("fit", TString::kIgnoreCase)) { // fit range - if (tokens->GetEntries() < 3) { + if (tokens.size() < 3) { error = true; } else { // fit given in time, i.e. fit , where , are given as doubles if (iter->fLine.Contains("fgb", TString::kIgnoreCase)) { // fit given in bins, i.e. fit fgb+n0 lgb-n1 // check 1st entry, i.e. fgb[+n0] - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - Ssiz_t idx = str.First("+"); - TString numStr = str; - if (idx > -1) { // '+' present hence extract n0 - numStr.Remove(0,idx+1); - if (numStr.IsFloat()) { - global.SetFitRangeOffset(numStr.Atoi(), 0); + std::string numStr = tokens[1]; + std::string::size_type pos = numStr.find('+'); + if (pos != std::string::npos) { // '+' present hence extract n0 + numStr = numStr.substr(pos+1); + if (PStringUtils::IsFloat(numStr)) { + global.SetFitRangeOffset(PStringUtils::ToInt(numStr), 0); } else { error = true; } @@ -3298,14 +3234,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) global.SetFitRangeOffset(0, 0); } // check 2nd entry, i.e. lgb[-n1] - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - idx = str.First("-"); - numStr = str; - if (idx > -1) { // '-' present hence extract n1 - numStr.Remove(0,idx+1); - if (numStr.IsFloat()) { - global.SetFitRangeOffset(numStr.Atoi(), 1); + numStr = tokens[2]; + pos = numStr.find('-'); + if (pos != std::string::npos) { // '-' present hence extract n1 + numStr = numStr.substr(pos+1); + if (PStringUtils::IsFloat(numStr)) { + global.SetFitRangeOffset(PStringUtils::ToInt(numStr), 1); } else { error = true; } @@ -3315,24 +3249,20 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) if (!error) global.SetFitRangeInBins(true); } else { // fit given in time, i.e. fit , where , are given as doubles - for (Int_t i=1; i<3; i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) - global.SetFitRange(str.Atof(), i-1); + for (UInt_t i=1; i<3; i++) { + if (PStringUtils::IsFloat(tokens[i])) + global.SetFitRange(PStringUtils::ToDouble(tokens[i]), i-1); else error = true; } } } } else if (iter->fLine.BeginsWith("packing", TString::kIgnoreCase)) { // packing - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival >= 0) { global.SetPacking(ival); } else { @@ -3343,27 +3273,19 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("deadtime-cor", TString::kIgnoreCase)) { // deadtime correction - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("no", TString::kIgnoreCase) || - !str.CompareTo("file", TString::kIgnoreCase) || - !str.CompareTo("estimate", TString::kIgnoreCase)) { - global.SetDeadTimeCorrection(str); + if (PStringUtils::IsEqualNoCase(tokens[1], "no") || + PStringUtils::IsEqualNoCase(tokens[1], "file") || + PStringUtils::IsEqualNoCase(tokens[1], "estimate")) { + global.SetDeadTimeCorrection(tokens[1].c_str()); } else { error = true; } } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } - ++iter; } @@ -3403,8 +3325,7 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) Bool_t runLinePresent = false; TString str, line; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; + std::vector tokens; UInt_t addT0Counter = 0; @@ -3423,14 +3344,7 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) str.Remove(idx); // tokenize line - tokens = str.Tokenize(" \t"); - if (!tokens) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::HandleRunEntry: **SEVERE ERROR** Couldn't tokenize Parameters in line " << iter->fLineNo << "\n\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } + tokens = PStringUtils::Split(str.Data(), " \t"); // copy of the current line line = iter->fLine; @@ -3451,29 +3365,26 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) // get run name, beamline, institute, and file-format // the path/filename could potentially contain spaces! Hence the run name needs to be reconstructed from the parsing - if (tokens->GetEntries() < 5) { + if (tokens.size() < 5) { error = true; } else { // run name - str = TString(""); - for (Int_t i=1; iGetEntries()-3; i++) { - ostr = dynamic_cast(tokens->At(i)); - str += ostr->GetString(); - if (iGetEntries()-4) - str += TString(" "); + std::string runName(""); + for (UInt_t i=1; i(tokens->At(tokens->GetEntries()-3)); - str = ostr->GetString(); + str = tokens[tokens.size()-3].c_str(); param.SetBeamline(str); // institute - ostr = dynamic_cast(tokens->At(tokens->GetEntries()-2)); - str = ostr->GetString(); + str = tokens[tokens.size()-2].c_str(); param.SetInstitute(str); // data file format - ostr = dynamic_cast(tokens->At(tokens->GetEntries()-1)); - str = ostr->GetString(); + str = tokens[tokens.size()-1].c_str(); param.SetFileFormat(str); } @@ -3495,24 +3406,20 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) } // get run name, beamline, institute, and file-format - if (tokens->GetEntries() < 5) { + if (tokens.size() < 5) { error = true; } else { // run name - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); + str = tokens[1].c_str(); param.SetRunName(str); // beamline - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); + str = tokens[2].c_str(); param.SetBeamline(str); // institute - ostr = dynamic_cast(tokens->At(3)); - str = ostr->GetString(); + str = tokens[3].c_str(); param.SetInstitute(str); // data file format - ostr = dynamic_cast(tokens->At(4)); - str = ostr->GetString(); + str = tokens[4].c_str(); param.SetFileFormat(str); } } @@ -3522,13 +3429,11 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - Int_t fittype = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + Int_t fittype = PStringUtils::ToInt(tokens[1]); if ((fittype == MSR_FITTYPE_SINGLE_HISTO) || (fittype == MSR_FITTYPE_SINGLE_HISTO_RRF) || (fittype == MSR_FITTYPE_ASYM) || @@ -3551,20 +3456,18 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival > 0) param.SetAlphaParamNo(ival); else error = true; - } else if (str.Contains("fun")) { + } else if (tokens[1].find("fun") != std::string::npos) { Int_t no; - if (FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, no)) + if (FilterNumber(tokens[1].c_str(), "fun", MSR_PARAM_FUN_OFFSET, no)) param.SetAlphaParamNo(no); else error = true; @@ -3579,20 +3482,18 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival > 0) param.SetBetaParamNo(ival); else - error = true; - } else if (str.Contains("fun")) { + error = true; + } else if (tokens[1].find("fun") != std::string::npos) { Int_t no; - if (FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, no)) + if (FilterNumber(tokens[1].c_str(), "fun", MSR_PARAM_FUN_OFFSET, no)) param.SetBetaParamNo(no); else error = true; @@ -3607,16 +3508,14 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - param.SetNormParamNo(str.Atoi()); - } else if (str.Contains("fun")) { + if (PStringUtils::IsInt(tokens[1])) { + param.SetNormParamNo(PStringUtils::ToInt(tokens[1])); + } else if (tokens[1].find("fun") != std::string::npos) { Int_t no; - if (FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, no)) + if (FilterNumber(tokens[1].c_str(), "fun", MSR_PARAM_FUN_OFFSET, no)) param.SetNormParamNo(no); else error = true; @@ -3631,13 +3530,11 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival > 0) param.SetBkgFitParamNo(ival); else @@ -3653,13 +3550,11 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival > 0) param.SetLifetimeParamNo(ival); else @@ -3683,11 +3578,9 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + for (UInt_t i=1; i= 0) param.SetMap(ival); else @@ -3716,7 +3609,7 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { PUIntVector group; @@ -3740,7 +3633,7 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { PUIntVector group; @@ -3764,14 +3657,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) - param.SetBkgFix(str.Atof(), i-1); + for (UInt_t i=1; iGetEntries() < 3) || (tokens->GetEntries() % 2 != 1)) { // odd number (>=3) of entries needed + if ((tokens.size() < 3) || (tokens.size() % 2 != 1)) { // odd number (>=3) of entries needed error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + for (UInt_t i=1; i 0) param.SetBkgRange(ival, i-1); else @@ -3807,14 +3696,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if ((tokens->GetEntries() < 3) || (tokens->GetEntries() % 2 != 1)) { // odd number (>=3) of entries needed + if ((tokens.size() < 3) || (tokens.size() % 2 != 1)) { // odd number (>=3) of entries needed error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + for (UInt_t i=1; i 0) param.SetDataRange(ival, i-1); else @@ -3831,14 +3718,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) { - dval = str.Atof(); + for (UInt_t i=1; i= 0.0) param.SetT0Bin(dval); else @@ -3855,14 +3740,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) { - dval = str.Atof(); + for (UInt_t i=1; i= 0.0) param.SetAddT0Bin(dval, addT0Counter, i-1); else @@ -3881,19 +3764,17 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 3) { + if (tokens.size() < 3) { error = true; } else { if (iter->fLine.Contains("fgb", TString::kIgnoreCase)) { // fit given in bins, i.e. fit fgb+n0 lgb-n1 // check 1st entry, i.e. fgb[+n0] - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - Ssiz_t idx = str.First("+"); - TString numStr = str; - if (idx > -1) { // '+' present hence extract n0 - numStr.Remove(0,idx+1); - if (numStr.IsFloat()) { - param.SetFitRangeOffset(numStr.Atoi(), 0); + std::string numStr = tokens[1]; + std::string::size_type pos = numStr.find('+'); + if (pos != std::string::npos) { // '+' present hence extract n0 + numStr = numStr.substr(pos+1); + if (PStringUtils::IsFloat(numStr)) { + param.SetFitRangeOffset(PStringUtils::ToInt(numStr), 0); } else { error = true; } @@ -3901,14 +3782,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) param.SetFitRangeOffset(0, 0); } // check 2nd entry, i.e. lgb[-n1] - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - idx = str.First("-"); - numStr = str; - if (idx > -1) { // '-' present hence extract n1 - numStr.Remove(0,idx+1); - if (numStr.IsFloat()) { - param.SetFitRangeOffset(numStr.Atoi(), 1); + numStr = tokens[2]; + pos = numStr.find('-'); + if (pos != std::string::npos) { // '-' present hence extract n1 + numStr = numStr.substr(pos+1); + if (PStringUtils::IsFloat(numStr)) { + param.SetFitRangeOffset(PStringUtils::ToInt(numStr), 1); } else { error = true; } @@ -3919,11 +3798,9 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (!error) param.SetFitRangeInBins(true); } else { // fit given in time, i.e. fit , where , are given as doubles - for (Int_t i=1; i<3; i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsFloat()) - param.SetFitRange(str.Atof(), i-1); + for (UInt_t i=1; i<3; i++) { + if (PStringUtils::IsFloat(tokens[i])) + param.SetFitRange(PStringUtils::ToDouble(tokens[i]), i-1); else error = true; } @@ -3936,13 +3813,11 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() != 2) { + if (tokens.size() != 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if (ival > 0) param.SetPacking(ival); else @@ -3958,15 +3833,13 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() < 2) { + if (tokens.size() < 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("no", TString::kIgnoreCase) || - !str.CompareTo("file", TString::kIgnoreCase) || - !str.CompareTo("estimate", TString::kIgnoreCase)) { - param.SetDeadTimeCorrection(str); + if (PStringUtils::IsEqualNoCase(tokens[1], "no") || + PStringUtils::IsEqualNoCase(tokens[1], "file") || + PStringUtils::IsEqualNoCase(tokens[1], "estimate")) { + param.SetDeadTimeCorrection(tokens[1].c_str()); } else { error = true; } @@ -3979,17 +3852,13 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following - if (tokens->GetEntries() != 3) { // xy-data x-label y-label + if (tokens.size() != 3) { // xy-data x-label y-label error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { // xy-data indices given - param.SetXDataIndex(str.Atoi()); // x-index - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { // xy-data indices given + param.SetXDataIndex(PStringUtils::ToInt(tokens[1])); // x-index + if (PStringUtils::IsInt(tokens[2])) { + ival = PStringUtils::ToInt(tokens[2]); if (ival > 0) param.SetYDataIndex(ival); // y-index else @@ -3998,20 +3867,14 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) error = true; } } else { // xy-data labels given + str = tokens[1].c_str(); param.SetXDataLabel(str); // x-label - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); + str = tokens[2].c_str(); param.SetYDataLabel(str); // y-label } } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } - ++iter; } @@ -4173,34 +4036,23 @@ Bool_t PMsrHandler::ParseFourierPhaseValueVector(PMsrFourierStructure &fourier, { Bool_t result = true; - TObjArray *tok = str.Tokenize(" ,;\t"); - if (tok == nullptr) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseValueVector: **ERROR** couldn't tokenize Fourier phase line.\n\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } + std::vector tok = PStringUtils::Split(str.Data(), " ,;\t"); // make sure there are enough tokens - if (tok->GetEntries() < 2) { + if (tok.size() < 2) { error = true; return false; } // convert all acceptable tokens - TObjString *ostr = nullptr; - TString sstr(""); - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tok->At(i)); - sstr = ostr->GetString(); - if (sstr.IsFloat()) { - fourier.fPhase.push_back(sstr.Atof()); + for (UInt_t i=1; i1) { // make sure that no 'phase val, parX' mixture is present fLastErrorMsg.str(""); - fLastErrorMsg.clear(); + fLastErrorMsg.clear(); fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseValueVector: **ERROR** in Fourier phase line.\n"; fLastErrorMsg << ">> Attempt to mix val, parX? This is currently not supported.\n\n"; std::cerr << fLastErrorMsg.str(); @@ -4210,11 +4062,6 @@ Bool_t PMsrHandler::ParseFourierPhaseValueVector(PMsrFourierStructure &fourier, } } - // clean up - if (tok) { - delete tok; - } - return result; } @@ -4239,42 +4086,32 @@ Bool_t PMsrHandler::ParseFourierPhaseParVector(PMsrFourierStructure &fourier, co Bool_t result = true; Int_t refCount = 0; - TObjArray *tok = str.Tokenize(" ,;\t"); - if (tok == nullptr) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParVector: **ERROR** couldn't tokenize Fourier phase line.\n\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } + std::vector tok = PStringUtils::Split(str.Data(), " ,;\t"); // make sure there are enough tokens - if (tok->GetEntries() < 2) { + if (tok.size() < 2) { error = true; return false; } // check that all tokens start with par - TString sstr; - for (Int_t i=1; iGetEntries(); i++) { - TObjString *ostr = dynamic_cast(tok->At(i)); - sstr = ostr->GetString(); - if (!sstr.BeginsWith("par")) { + for (UInt_t i=1; i> PMsrHandler::ParseFourierPhaseParVector: **ERROR** found unhandable token '" << sstr << "'\n"; + fLastErrorMsg.clear(); + fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParVector: **ERROR** found unhandable token '" << tok[i] << "'\n"; std::cerr << fLastErrorMsg.str(); error = true; result = false; break; } - if (sstr.BeginsWith("parR")) { + if (tok[i].rfind("parR", 0) == 0) { refCount++; } // rule out par(X, offset, #Param) syntax - if (sstr.BeginsWith("par(")) { + if (tok[i].rfind("par(", 0) == 0) { result = false; break; } @@ -4282,7 +4119,7 @@ Bool_t PMsrHandler::ParseFourierPhaseParVector(PMsrFourierStructure &fourier, co if (refCount > 1) { fLastErrorMsg.str(""); - fLastErrorMsg.clear(); + fLastErrorMsg.clear(); fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParVector: **ERROR** found multiple parR's! Only one reference phase is accepted.\n"; std::cerr << fLastErrorMsg.str(); result = false; @@ -4291,22 +4128,21 @@ Bool_t PMsrHandler::ParseFourierPhaseParVector(PMsrFourierStructure &fourier, co // check that token has the form parX, where X is an int Int_t rmNoOf = 3; if (result != false) { - for (Int_t i=1; iGetEntries(); i++) { - TObjString *ostr = dynamic_cast(tok->At(i)); - sstr = ostr->GetString(); + for (UInt_t i=1; i> PMsrHandler::ParseFourierPhaseParVector: **ERROR** found token '" << ostr->GetString() << "' which is not parX with X an integer.\n"; + fLastErrorMsg.clear(); + fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParVector: **ERROR** found token '" << tok[i] << "' which is not parX with X an integer.\n"; std::cerr << fLastErrorMsg.str(); fourier.fPhaseParamNo.clear(); error = true; @@ -4315,17 +4151,12 @@ Bool_t PMsrHandler::ParseFourierPhaseParVector(PMsrFourierStructure &fourier, co } } - if (fourier.fPhaseParamNo.size() == tok->GetEntries()-1) { // everything as expected + if (fourier.fPhaseParamNo.size() == tok.size()-1) { // everything as expected result = true; } else { result = false; } - // clean up - if (tok) { - delete tok; - } - return result; } @@ -4376,71 +4207,53 @@ Bool_t PMsrHandler::ParseFourierPhaseParIterVector(PMsrFourierStructure &fourier wstr.Remove(idx, wstr.Length()-idx); // tokenize rest which should have the form 'X0, offset, #Param' - TObjArray *tok = wstr.Tokenize(",;"); - if (tok == nullptr) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** tokenize failed.\n"; - std::cerr << fLastErrorMsg.str(); - error = true; - return false; - } + std::vector tok = PStringUtils::Split(wstr.Data(), ",;"); // check for proper number of expected elements - if (tok->GetEntries() != 3) { + if (tok.size() != 3) { fLastErrorMsg.str(""); - fLastErrorMsg.clear(); + fLastErrorMsg.clear(); fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** wrong syntax for the expected par(X0, offset, #param).\n"; std::cerr << fLastErrorMsg.str(); error = true; - delete tok; return false; } Int_t x0, offset, noParam; // get X0 - TObjString *ostr = dynamic_cast(tok->At(0)); - wstr = ostr->GetString(); - if (wstr.IsDigit()) { - x0 = wstr.Atoi(); + if (PStringUtils::IsInt(tok[0])) { + x0 = PStringUtils::ToInt(tok[0]); } else { fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** X0='" << wstr << "' is not an integer.\n"; + fLastErrorMsg.clear(); + fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** X0='" << tok[0] << "' is not an integer.\n"; std::cerr << fLastErrorMsg.str(); error = true; - delete tok; return false; } // get offset - ostr = dynamic_cast(tok->At(1)); - wstr = ostr->GetString(); - if (wstr.IsDigit()) { - offset = wstr.Atoi(); + if (PStringUtils::IsInt(tok[1])) { + offset = PStringUtils::ToInt(tok[1]); } else { fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** offset='" << wstr << "' is not an integer.\n"; + fLastErrorMsg.clear(); + fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** offset='" << tok[1] << "' is not an integer.\n"; std::cerr << fLastErrorMsg.str(); error = true; - delete tok; return false; } // get noParam - ostr = dynamic_cast(tok->At(2)); - wstr = ostr->GetString(); - if (wstr.IsDigit()) { - noParam = wstr.Atoi(); + if (PStringUtils::IsInt(tok[2])) { + noParam = PStringUtils::ToInt(tok[2]); } else { fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** #Param='" << wstr << "' is not an integer.\n"; + fLastErrorMsg.clear(); + fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** #Param='" << tok[2] << "' is not an integer.\n"; std::cerr << fLastErrorMsg.str(); error = true; - delete tok; return false; } @@ -4453,11 +4266,6 @@ Bool_t PMsrHandler::ParseFourierPhaseParIterVector(PMsrFourierStructure &fourier for (Int_t i=0; i tokens; + TString pcStr=TString(""); Int_t ival; iter = lines.begin(); while ((iter != lines.end()) && !error) { // tokenize line - tokens = iter->fLine.Tokenize(" \t"); - if (!tokens) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::HandleFourierEntry: **SEVERE ERROR** Couldn't tokenize Parameters in line " << iter->fLineNo << "\n\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } + tokens = PStringUtils::Split(iter->fLine.Data(), " \t"); if (iter->fLine.BeginsWith("units", TString::kIgnoreCase)) { // units - if (tokens->GetEntries() < 2) { // units are missing + if (tokens.size() < 2) { // units are missing error = true; continue; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("gauss", TString::kIgnoreCase)) { + if (PStringUtils::IsEqualNoCase(tokens[1], "gauss")) { fourier.fUnits = FOURIER_UNIT_GAUSS; - } else if (!str.CompareTo("tesla", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "tesla")) { fourier.fUnits = FOURIER_UNIT_TESLA; - } else if (!str.CompareTo("mhz", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "mhz")) { fourier.fUnits = FOURIER_UNIT_FREQ; - } else if (!str.CompareTo("mc/s", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "mc/s")) { fourier.fUnits = FOURIER_UNIT_CYCLES; } else { error = true; @@ -4527,14 +4325,12 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("fourier_power", TString::kIgnoreCase)) { // fourier power (zero padding) - if (tokens->GetEntries() < 2) { // fourier power exponent is missing + if (tokens.size() < 2) { // fourier power exponent is missing error = true; continue; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + ival = PStringUtils::ToInt(tokens[1]); if ((ival >= 0) && (ival <= 20)) { fourier.fFourierPower = ival; } else { // fourier power out of range @@ -4547,15 +4343,13 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("dc-corrected", TString::kIgnoreCase)) { // dc-corrected - if (tokens->GetEntries() < 2) { // dc-corrected tag is missing + if (tokens.size() < 2) { // dc-corrected tag is missing error = true; continue; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("true", TString::kIgnoreCase) || !str.CompareTo("1")) { + if (PStringUtils::IsEqualNoCase(tokens[1], "true") || (tokens[1] == "1")) { fourier.fDCCorrected = true; - } else if (!str.CompareTo("false", TString::kIgnoreCase) || !str.CompareTo("0")) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "false") || (tokens[1] == "0")) { fourier.fDCCorrected = false; } else { // unrecognized dc-corrected tag error = true; @@ -4563,19 +4357,17 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("apodization", TString::kIgnoreCase)) { // apodization - if (tokens->GetEntries() < 2) { // apodization tag is missing + if (tokens.size() < 2) { // apodization tag is missing error = true; continue; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("none", TString::kIgnoreCase)) { + if (PStringUtils::IsEqualNoCase(tokens[1], "none")) { fourier.fApodization = FOURIER_APOD_NONE; - } else if (!str.CompareTo("weak", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "weak")) { fourier.fApodization = FOURIER_APOD_WEAK; - } else if (!str.CompareTo("medium", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "medium")) { fourier.fApodization = FOURIER_APOD_MEDIUM; - } else if (!str.CompareTo("strong", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "strong")) { fourier.fApodization = FOURIER_APOD_STRONG; } else { // unrecognized apodization tag error = true; @@ -4583,23 +4375,21 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("plot", TString::kIgnoreCase)) { // plot tag - if (tokens->GetEntries() < 2) { // plot tag is missing + if (tokens.size() < 2) { // plot tag is missing error = true; continue; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("real", TString::kIgnoreCase)) { + if (PStringUtils::IsEqualNoCase(tokens[1], "real")) { fourier.fPlotTag = FOURIER_PLOT_REAL; - } else if (!str.CompareTo("imag", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "imag")) { fourier.fPlotTag = FOURIER_PLOT_IMAG; - } else if (!str.CompareTo("real_and_imag", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "real_and_imag")) { fourier.fPlotTag = FOURIER_PLOT_REAL_AND_IMAG; - } else if (!str.CompareTo("power", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "power")) { fourier.fPlotTag = FOURIER_PLOT_POWER; - } else if (!str.CompareTo("phase", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "phase")) { fourier.fPlotTag = FOURIER_PLOT_PHASE; - } else if (!str.CompareTo("phase_opt_real", TString::kIgnoreCase)) { + } else if (PStringUtils::IsEqualNoCase(tokens[1], "phase_opt_real")) { fourier.fPlotTag = FOURIER_PLOT_PHASE_OPT_REAL; } else { // unrecognized plot tag error = true; @@ -4607,7 +4397,7 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) } } } else if (iter->fLine.BeginsWith("phase", TString::kIgnoreCase)) { // phase - if (tokens->GetEntries() < 2) { // phase value(s)/par(s) is(are) missing + if (tokens.size() < 2) { // phase value(s)/par(s) is(are) missing error = true; continue; } else { @@ -4678,15 +4468,13 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) // available at this point. pcStr = iter->fLine; } else if (iter->fLine.BeginsWith("range", TString::kIgnoreCase)) { // fourier plot range - if (tokens->GetEntries() < 3) { // plot range values are missing + if (tokens.size() < 3) { // plot range values are missing error = true; continue; } else { for (UInt_t i=0; i<2; i++) { - ostr = dynamic_cast(tokens->At(i+1)); - str = ostr->GetString(); - if (str.IsFloat()) { - fourier.fPlotRange[i] = str.Atof(); + if (PStringUtils::IsFloat(tokens[i+1])) { + fourier.fPlotRange[i] = PStringUtils::ToDouble(tokens[i+1]); } else { error = true; continue; @@ -4699,31 +4487,17 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) continue; } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } - ++iter; } - // clean up after error - if (tokens) { - delete tokens; - tokens = nullptr; - } - // handle range_for_phase_correction if present if ((pcStr.Length() != 0) && !error) { // tokenize line - tokens = pcStr.Tokenize(" \t"); + tokens = PStringUtils::Split(pcStr.Data(), " \t"); - switch (tokens->GetEntries()) { + switch (tokens.size()) { case 2: - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("all", TString::kIgnoreCase)) { + if (PStringUtils::IsEqualNoCase(tokens[1], "all")) { fourier.fRangeForPhaseCorrection[0] = fourier.fPlotRange[0]; fourier.fRangeForPhaseCorrection[1] = fourier.fPlotRange[1]; } else { @@ -4732,10 +4506,8 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) break; case 3: for (UInt_t i=0; i<2; i++) { - ostr = dynamic_cast(tokens->At(i+1)); - str = ostr->GetString(); - if (str.IsFloat()) { - fourier.fRangeForPhaseCorrection[i] = str.Atof(); + if (PStringUtils::IsFloat(tokens[i+1])) { + fourier.fRangeForPhaseCorrection[i] = PStringUtils::ToDouble(tokens[i+1]); } else { error = true; } @@ -4745,12 +4517,6 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) error = true; break; } - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } if (error) { @@ -4803,9 +4569,7 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) PMsrLines::iterator iter1; PMsrLines::iterator iter2; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; - TString str; + std::vector tokens; if (lines.empty()) { std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry(): **WARNING**: There is no PLOT block! Do you really want this?"; @@ -4848,29 +4612,15 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) line.Resize(line.First('#')); if (line.Contains("PLOT")) { // handle plot header - tokens = line.Tokenize(" \t"); - if (!tokens) { - fLastErrorMsg.str(""); - fLastErrorMsg.clear(); - fLastErrorMsg << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize PLOT in line " << iter1->fLineNo << "\n\n"; - std::cerr << fLastErrorMsg.str(); - return false; - } - if (tokens->GetEntries() < 2) { // plot type missing + tokens = PStringUtils::Split(line.Data(), " \t"); + if (tokens.size() < 2) { // plot type missing error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) - param.fPlotType = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) + param.fPlotType = PStringUtils::ToInt(tokens[1]); else error = true; } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("lifetimecorrection", TString::kIgnoreCase)) { param.fLifeTimeCorrection = true; } else if (line.Contains("runs", TString::kIgnoreCase)) { // handle plot runs @@ -4917,56 +4667,38 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) param.fYmin.clear(); param.fYmax.clear(); - tokens = line.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize PLOT in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } - if ((tokens->GetEntries() != 3) && (tokens->GetEntries() != 5)) { + tokens = PStringUtils::Split(line.Data(), " \t"); + if ((tokens.size() != 3) && (tokens.size() != 5)) { error = true; } else { // handle t_min - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fTmin.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[1])) + param.fTmin.push_back(PStringUtils::ToDouble(tokens[1])); else error = true; // handle t_max - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fTmax.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[2])) + param.fTmax.push_back(PStringUtils::ToDouble(tokens[2])); else error = true; - if (tokens->GetEntries() == 5) { // y-axis interval given as well + if (tokens.size() == 5) { // y-axis interval given as well // handle y_min - ostr = dynamic_cast(tokens->At(3)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fYmin.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[3])) + param.fYmin.push_back(PStringUtils::ToDouble(tokens[3])); else error = true; // handle y_max - ostr = dynamic_cast(tokens->At(4)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fYmax.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[4])) + param.fYmax.push_back(PStringUtils::ToDouble(tokens[4])); else error = true; } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("sub_ranges", TString::kIgnoreCase)) { // remove previous entries param.fTmin.clear(); @@ -4974,102 +4706,68 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) param.fYmin.clear(); param.fYmax.clear(); - tokens = line.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize PLOT in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } - if ((tokens->GetEntries() != static_cast(2*param.fRuns.size() + 1)) && (tokens->GetEntries() != static_cast(2*param.fRuns.size() + 3))) { + tokens = PStringUtils::Split(line.Data(), " \t"); + if ((tokens.size() != 2*param.fRuns.size() + 1) && (tokens.size() != 2*param.fRuns.size() + 3)) { error = true; } else { // get all the times for (UInt_t i=0; i(tokens->At(2*i+1)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fTmin.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[2*i+1])) + param.fTmin.push_back(PStringUtils::ToDouble(tokens[2*i+1])); else error = true; // handle t_max - ostr = dynamic_cast(tokens->At(2*i+2)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fTmax.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[2*i+2])) + param.fTmax.push_back(PStringUtils::ToDouble(tokens[2*i+2])); else error = true; } // get y-range if present - if (tokens->GetEntries() == static_cast(2*param.fRuns.size() + 3)) { + if (tokens.size() == 2*param.fRuns.size() + 3) { // handle y_min - ostr = dynamic_cast(tokens->At(2*param.fRuns.size()+1)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fYmin.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[2*param.fRuns.size()+1])) + param.fYmin.push_back(PStringUtils::ToDouble(tokens[2*param.fRuns.size()+1])); else error = true; // handle y_max - ostr = dynamic_cast(tokens->At(2*param.fRuns.size()+2)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fYmax.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[2*param.fRuns.size()+2])) + param.fYmax.push_back(PStringUtils::ToDouble(tokens[2*param.fRuns.size()+2])); else error = true; } } - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("use_fit_ranges", TString::kIgnoreCase)) { param.fUseFitRanges = true; // check if y-ranges are given - tokens = line.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize PLOT in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } + tokens = PStringUtils::Split(line.Data(), " \t"); - if (tokens->GetEntries() == 3) { // i.e. use_fit_ranges ymin ymax + if (tokens.size() == 3) { // i.e. use_fit_ranges ymin ymax // handle y_min - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fYmin.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[1])) + param.fYmin.push_back(PStringUtils::ToDouble(tokens[1])); else error = true; // handle y_max - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - if (str.IsFloat()) - param.fYmax.push_back(static_cast(str.Atof())); + if (PStringUtils::IsFloat(tokens[2])) + param.fYmax.push_back(PStringUtils::ToDouble(tokens[2])); else error = true; } - if ((tokens->GetEntries() != 1) && (tokens->GetEntries() != 3)) { + if ((tokens.size() != 1) && (tokens.size() != 3)) { std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **WARNING** use_fit_ranges with undefined additional parameters in line " << iter1->fLineNo; std::cerr << std::endl << ">> Will ignore this PLOT block command line, sorry."; std::cerr << std::endl << ">> Proper syntax: use_fit_ranges [ymin ymax]"; std::cerr << std::endl << ">> Found: '" << iter1->fLine.Data() << "'" << std::endl; } - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (iter1->fLine.Contains("logx", TString::kIgnoreCase)) { param.fLogX = true; } else if (iter1->fLine.Contains("logy", TString::kIgnoreCase)) { @@ -5077,19 +4775,12 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) } else if (iter1->fLine.Contains("lifetimecorrection", TString::kIgnoreCase)) { param.fLifeTimeCorrection = true; } else if (iter1->fLine.Contains("view_packing", TString::kIgnoreCase)) { - tokens = iter1->fLine.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize view_packing in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } - if (tokens->GetEntries() != 2) { + tokens = PStringUtils::Split(iter1->fLine.Data(), " \t"); + if (tokens.size() != 2) { error = true; } else { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - Int_t val = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + Int_t val = PStringUtils::ToInt(tokens[1]); if (val > 0) param.fViewPacking = val; else @@ -5098,74 +4789,47 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) error = true; } } - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (iter1->fLine.Contains("rrf_freq", TString::kIgnoreCase)) { // expected entry: rrf_freq value unit // allowed units: kHz, MHz, Mc/s, G, T - tokens = iter1->fLine.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize rrf_freq in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } - if (tokens->GetEntries() != 3) { + tokens = PStringUtils::Split(iter1->fLine.Data(), " \t"); + if (tokens.size() != 3) { error = true; } else { // get rrf frequency - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsFloat()) { - param.fRRFFreq = str.Atof(); + if (PStringUtils::IsFloat(tokens[1])) { + param.fRRFFreq = PStringUtils::ToDouble(tokens[1]); } else { error = true; } // get unit - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); - if (str.Contains("kHz", TString::kIgnoreCase)) + if (PStringUtils::ContainsNoCase(tokens[2], "kHz")) param.fRRFUnit = RRF_UNIT_kHz; - else if (str.Contains("MHz", TString::kIgnoreCase)) + else if (PStringUtils::ContainsNoCase(tokens[2], "MHz")) param.fRRFUnit = RRF_UNIT_MHz; - else if (str.Contains("Mc/s", TString::kIgnoreCase)) + else if (PStringUtils::ContainsNoCase(tokens[2], "Mc/s")) param.fRRFUnit = RRF_UNIT_Mcs; - else if (str.Contains("G", TString::kIgnoreCase)) + else if (PStringUtils::ContainsNoCase(tokens[2], "G")) param.fRRFUnit = RRF_UNIT_G; - else if (str.Contains("T", TString::kIgnoreCase)) + else if (PStringUtils::ContainsNoCase(tokens[2], "T")) param.fRRFUnit = RRF_UNIT_T; else error = true; } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (iter1->fLine.Contains("rrf_phase", TString::kIgnoreCase)) { // expected entry: rrf_phase value. value given in units of degree. or // rrf_phase parX. where X is the parameter number, e.g. par3 - tokens = iter1->fLine.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize rrf_phase in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } - if (tokens->GetEntries() != 2) { + tokens = PStringUtils::Split(iter1->fLine.Data(), " \t"); + if (tokens.size() != 2) { error = true; } else { // get rrf phase - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsFloat()) { - param.fRRFPhase = str.Atof(); + if (PStringUtils::IsFloat(tokens[1])) { + param.fRRFPhase = PStringUtils::ToDouble(tokens[1]); } else { - if (str.BeginsWith("par", TString::kIgnoreCase)) { // parameter value + if (PStringUtils::BeginsWithNoCase(tokens[1], "par")) { // parameter value Int_t no = 0; - if (FilterNumber(str, "par", 0, no)) { + if (FilterNumber(tokens[1].c_str(), "par", 0, no)) { // check that the parameter is in range if (static_cast(fParam.size()) < no) { error = true; @@ -5181,36 +4845,19 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) } } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (iter1->fLine.Contains("rrf_packing", TString::kIgnoreCase)) { // expected entry: rrf_phase value. value given in units of degree - tokens = iter1->fLine.Tokenize(" \t"); - if (!tokens) { - std::cerr << std::endl << ">> PMsrHandler::HandlePlotEntry: **SEVERE ERROR** Couldn't tokenize rrf_packing in line " << iter1->fLineNo; - std::cerr << std::endl << std::endl; - return false; - } - if (tokens->GetEntries() != 2) { + tokens = PStringUtils::Split(iter1->fLine.Data(), " \t"); + if (tokens.size() != 2) { error = true; } else { // get rrf packing - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (str.IsDigit()) { - param.fRRFPacking = str.Atoi(); + if (PStringUtils::IsInt(tokens[1])) { + param.fRRFPacking = PStringUtils::ToInt(tokens[1]); } else { error = true; } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } else { error = true; } @@ -5448,8 +5095,7 @@ UInt_t PMsrHandler::GetNoOfFitParameters(UInt_t idx) PUIntVector paramVector; PUIntVector funVector; PUIntVector mapVector; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; + std::vector tokens; TString str; UInt_t k, dval; Int_t status, pos; @@ -5498,42 +5144,31 @@ UInt_t PMsrHandler::GetNoOfFitParameters(UInt_t idx) if (pos >= 0) str.Resize(pos); // tokenize - tokens = str.Tokenize(" \t"); - if (!tokens) { - mapVector.clear(); - funVector.clear(); - paramVector.clear(); - return 0; - } + tokens = PStringUtils::Split(str.Data(), " \t"); - for (Int_t j=0; jGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); + for (UInt_t j=0; jGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); + for (UInt_t j=0; j tokens; TString str; Int_t ival, funNo; @@ -5664,33 +5290,23 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi str.ToLower(); // tokenize string - tokens = str.Tokenize(" \t"); - if (!tokens) - continue; + tokens = PStringUtils::Split(str.Data(), " \t"); // filter param no, map no, and fun no - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); - if (str.IsDigit()) { // parameter number - ival = str.Atoi(); + for (UInt_t i=0; i 0) && (ival < static_cast(fParam.size())+1)) { fParamInUse[ival-1]++; } - } else if (str.Contains("map")) { // map - if (FilterNumber(str, "map", MSR_PARAM_MAP_OFFSET, ival)) + } else if (tokens[i].find("map") != std::string::npos) { // map + if (FilterNumber(tokens[i].c_str(), "map", MSR_PARAM_MAP_OFFSET, ival)) map.push_back(ival-MSR_PARAM_MAP_OFFSET); - } else if (str.Contains("fun")) { // fun - if (FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, ival)) + } else if (tokens[i].find("fun") != std::string::npos) { // fun + if (FilterNumber(tokens[i].c_str(), "fun", MSR_PARAM_FUN_OFFSET, ival)) fun.push_back(ival-MSR_PARAM_FUN_OFFSET); } } - - // delete tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } } // go through all the function lines: 1st time ----------------------------- @@ -5703,14 +5319,12 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi // everything to lower case str.ToLower(); - tokens = str.Tokenize(" /t"); - if (!tokens) + tokens = PStringUtils::Split(str.Data(), " /t"); + if (tokens.empty()) continue; // filter fun number - ostr = dynamic_cast(tokens->At(0)); - str = ostr->GetString(); - if (!FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, funNo)) + if (!FilterNumber(tokens[0].c_str(), "fun", MSR_PARAM_FUN_OFFSET, funNo)) continue; funNo -= MSR_PARAM_FUN_OFFSET; @@ -5759,12 +5373,6 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi break; // since function was found, break the loop } } - - // delete tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } } // go through all the run block lines ------------------------------------- @@ -5783,61 +5391,42 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi str.Contains("norm") || str.Contains("backgr.fit") || str.Contains("lifetime ")) { // tokenize string - tokens = str.Tokenize(" \t"); - if (!tokens) - continue; - if (tokens->GetEntries()<2) + tokens = PStringUtils::Split(str.Data(), " \t"); + if (tokens.size()<2) continue; - ostr = dynamic_cast(tokens->At(1)); // parameter number or function - str = ostr->GetString(); + std::string tok1 = tokens[1]; // parameter number or function // check if parameter number - if (str.IsDigit()) { - ival = str.Atoi(); + if (PStringUtils::IsInt(tok1)) { + ival = PStringUtils::ToInt(tok1); fParamInUse[ival-1]++; } // check if fun - if (str.Contains("fun")) { - if (FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, ival)) { + if (tok1.find("fun") != std::string::npos) { + if (FilterNumber(tok1.c_str(), "fun", MSR_PARAM_FUN_OFFSET, ival)) { fun.push_back(ival-MSR_PARAM_FUN_OFFSET); } } - - // delete tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } } // handle the maps if (str.Contains("map")) { // tokenize string - tokens = str.Tokenize(" \t"); - if (!tokens) - continue; + tokens = PStringUtils::Split(str.Data(), " \t"); // get the parameter number via map for (UInt_t i=0; iGetEntries()) { - ostr = dynamic_cast(tokens->At(map[i])); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (map[i] < static_cast(tokens.size())) { + if (PStringUtils::IsInt(tokens[map[i]])) { + ival = PStringUtils::ToInt(tokens[map[i]]); if (ival > 0) { fParamInUse[ival-1]++; // this is OK since map is ranging from 1 .. } } } } - - // delete tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } } } @@ -5851,14 +5440,12 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi // everything to lower case str.ToLower(); - tokens = str.Tokenize(" /t"); - if (!tokens) + tokens = PStringUtils::Split(str.Data(), " /t"); + if (tokens.empty()) continue; // filter fun number - ostr = dynamic_cast(tokens->At(0)); - str = ostr->GetString(); - if (!FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, funNo)) + if (!FilterNumber(tokens[0].c_str(), "fun", MSR_PARAM_FUN_OFFSET, funNo)) continue; funNo -= MSR_PARAM_FUN_OFFSET; @@ -5906,12 +5493,6 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi } } } - - // delete tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } } // go through all the run block lines 2nd time to filter remaining maps @@ -5927,31 +5508,21 @@ void PMsrHandler::FillParameterInUse(PMsrLines &theory, PMsrLines &funcs, PMsrLi // handle the maps if (str.Contains("map")) { // tokenize string - tokens = str.Tokenize(" \t"); - if (!tokens) - continue; + tokens = PStringUtils::Split(str.Data(), " \t"); // get the parameter number via map for (UInt_t i=0; iGetEntries()) { - ostr = dynamic_cast(tokens->At(map[i])); - str = ostr->GetString(); - if (str.IsDigit()) { - ival = str.Atoi(); + if (map[i] < static_cast(tokens.size())) { + if (PStringUtils::IsInt(tokens[map[i]])) { + ival = PStringUtils::ToInt(tokens[map[i]]); if (ival > 0) { fParamInUse[ival-1]++; // this is OK since map is ranging from 1 .. } } } } - - // delete tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } } } @@ -6367,9 +5938,7 @@ Bool_t PMsrHandler::CheckMaps() PIntVector mapBlock; PIntVector mapLineNo; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; - TString str; + std::vector tokens; Int_t no; @@ -6377,23 +5946,16 @@ Bool_t PMsrHandler::CheckMaps() for (UInt_t i=0; iGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); - if (str.Contains("map", TString::kIgnoreCase)) { - if (FilterNumber(str, "map", MSR_PARAM_MAP_OFFSET, no)) { + tokens = PStringUtils::Split(fTheory[i].fLine.Data(), " \t"); + for (UInt_t j=0; jGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); - if (str.Contains("map", TString::kIgnoreCase)) { - if (FilterNumber(str, "map", MSR_PARAM_MAP_OFFSET, no)) { + tokens = PStringUtils::Split(fFunctions[i].fLine.Data(), " \t"); + for (UInt_t j=0; j tokens; TString str; Int_t no; @@ -6490,23 +6044,16 @@ Bool_t PMsrHandler::CheckFuncs() for (UInt_t i=0; iGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); - if (str.Contains("fun", TString::kIgnoreCase)) { - if (FilterNumber(str, "fun", MSR_PARAM_FUN_OFFSET, no)) { + tokens = PStringUtils::Split(fTheory[i].fLine.Data(), " \t"); + for (UInt_t j=0; j +#include + +#include "PStringUtils.h" + +//-------------------------------------------------------------------------- +// Split (static) +//-------------------------------------------------------------------------- +/** + *

Splits a string into tokens on any character contained in delimiters, + * skipping empty tokens. Mirrors the semantics of TString::Tokenize(). + * + * \param str input string to be tokenized + * \param delimiters set of delimiter characters + * \return vector of tokens (without the delimiters) + */ +std::vector PStringUtils::Split(const std::string &str, const std::string &delimiters) +{ + std::vector tokens; + std::string::size_type start = str.find_first_not_of(delimiters); + while (start != std::string::npos) { + std::string::size_type end = str.find_first_of(delimiters, start); + if (end == std::string::npos) { + tokens.push_back(str.substr(start)); + break; + } + tokens.push_back(str.substr(start, end - start)); + start = str.find_first_not_of(delimiters, end); + } + return tokens; +} + +//-------------------------------------------------------------------------- +// IsInt (static) +//-------------------------------------------------------------------------- +/** + *

Returns true if the string is a non-empty sequence of decimal digits + * only. Mirrors the semantics of TString::IsDigit(). + * + * \param str string to be checked + * \return true if str consists of digits only + */ +bool PStringUtils::IsInt(const std::string &str) +{ + // mirror TString::IsDigit(): all characters must be digits or whitespace, + // and there must be at least one digit (surrounding/embedded whitespace is + // tolerated, e.g. for tokens split on ',' or ';' only). + bool hasDigit = false; + for (char c : str) { + if (std::isdigit(static_cast(c))) + hasDigit = true; + else if (!std::isspace(static_cast(c))) + return false; + } + return hasDigit; +} + +//-------------------------------------------------------------------------- +// IsFloat (static) +//-------------------------------------------------------------------------- +/** + *

Returns true if the string is a complete integer or floating point + * literal (optionally signed, with decimal point and/or exponent). Mirrors + * the semantics of TString::IsFloat() for the relevant cases. + * + * \param str string to be checked + * \return true if str is a valid number + */ +bool PStringUtils::IsFloat(const std::string &str) +{ + // mirror TString::IsFloat(): surrounding whitespace is ignored (e.g. for + // tokens split on ',' or ';' only), then a complete number is required. + const std::string ws(" \t\n\r\f\v"); + std::string::size_type b = str.find_first_not_of(ws); + if (b == std::string::npos) + return false; + std::string::size_type e = str.find_last_not_of(ws); + const std::string t = str.substr(b, e - b + 1); + + std::string::size_type i = 0; + if (t[i] == '+' || t[i] == '-') + ++i; + // reject things like "inf"/"nan" which strtod would otherwise accept + if (i >= t.size() || !(std::isdigit(static_cast(t[i])) || t[i] == '.')) + return false; + const char *begin = t.c_str(); + char *end = nullptr; + std::strtod(begin, &end); + return end == begin + t.size(); +} + +//-------------------------------------------------------------------------- +// ToInt (static) +//-------------------------------------------------------------------------- +/** + *

Converts the leading part of the string to an int (base 10), mirroring + * TString::Atoi(). Returns 0 if no conversion is possible. + * + * \param str string to be converted + * \return converted integer value + */ +int PStringUtils::ToInt(const std::string &str) +{ + return static_cast(std::strtol(str.c_str(), nullptr, 10)); +} + +//-------------------------------------------------------------------------- +// ToDouble (static) +//-------------------------------------------------------------------------- +/** + *

Converts the leading part of the string to a double, mirroring + * TString::Atof(). Returns 0.0 if no conversion is possible. + * + * \param str string to be converted + * \return converted double value + */ +double PStringUtils::ToDouble(const std::string &str) +{ + return std::strtod(str.c_str(), nullptr); +} + +//-------------------------------------------------------------------------- +// IsEqualNoCase (static) +//-------------------------------------------------------------------------- +/** + *

Case-insensitive full-string equality, mirroring + * TString::CompareTo(..., TString::kIgnoreCase) == 0. + * + * \param a first string + * \param b second string + * \return true if a and b are equal ignoring case + */ +bool PStringUtils::IsEqualNoCase(const std::string &a, const std::string &b) +{ + if (a.size() != b.size()) + return false; + for (std::string::size_type i = 0; i < a.size(); ++i) { + if (std::tolower(static_cast(a[i])) != + std::tolower(static_cast(b[i]))) + return false; + } + return true; +} + +//-------------------------------------------------------------------------- +// ContainsNoCase (static) +//-------------------------------------------------------------------------- +/** + *

Case-insensitive substring search, mirroring + * TString::Contains(..., TString::kIgnoreCase). + * + * \param haystack string to be searched in + * \param needle substring to be searched for + * \return true if needle is contained in haystack ignoring case + */ +bool PStringUtils::ContainsNoCase(const std::string &haystack, const std::string &needle) +{ + if (needle.empty()) + return true; + if (needle.size() > haystack.size()) + return false; + auto toLower = [](unsigned char c) { return std::tolower(c); }; + for (std::string::size_type i = 0; i + needle.size() <= haystack.size(); ++i) { + std::string::size_type j = 0; + for (; j < needle.size(); ++j) { + if (toLower(haystack[i+j]) != toLower(needle[j])) + break; + } + if (j == needle.size()) + return true; + } + return false; +} + +//-------------------------------------------------------------------------- +// BeginsWithNoCase (static) +//-------------------------------------------------------------------------- +/** + *

Case-insensitive prefix test, mirroring + * TString::BeginsWith(..., TString::kIgnoreCase). + * + * \param str string to be tested + * \param prefix prefix to be searched for + * \return true if str starts with prefix ignoring case + */ +bool PStringUtils::BeginsWithNoCase(const std::string &str, const std::string &prefix) +{ + if (prefix.size() > str.size()) + return false; + for (std::string::size_type i = 0; i < prefix.size(); ++i) { + if (std::tolower(static_cast(str[i])) != + std::tolower(static_cast(prefix[i]))) + return false; + } + return true; +} diff --git a/src/include/PStringUtils.h b/src/include/PStringUtils.h new file mode 100644 index 00000000..8bd71cc0 --- /dev/null +++ b/src/include/PStringUtils.h @@ -0,0 +1,137 @@ +/*************************************************************************** + + PStringUtils.h + + Author: Andreas Suter + e-mail: andreas.suter@psi.ch + +***************************************************************************/ + +/*************************************************************************** + * Copyright (C) 2007-2026 by Andreas Suter * + * andreas.suter@psi.ch * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _PSTRINGUTILS_H_ +#define _PSTRINGUTILS_H_ + +#include +#include + +//------------------------------------------------------------- +/** + * \brief Lightweight, dependency-free string utilities (pure C++17). + * + * PStringUtils collects small string helpers used throughout the musrfit + * suite, in particular for tokenizing and parsing the plain-text MSR file + * format. The implementation deliberately relies only on the C++ standard + * library (no ROOT) so that it can be reused freely. + * + * The provided helpers replicate the semantics of the corresponding + * ROOT TString methods that were previously used: + * - Split replaces TString::Tokenize() (+ TObjArray/TObjString) + * - IsInt replaces TString::IsDigit() + * - IsFloat replaces TString::IsFloat() + * - ToInt replaces TString::Atoi() + * - ToDouble replaces TString::Atof() + * - IsEqualNoCase replaces TString::CompareTo(..., TString::kIgnoreCase) + * + * All methods are static; the class is a pure namespace-like utility. + */ +class PStringUtils +{ + public: + /** + *

Splits a string into tokens on any character contained in + * delimiters, skipping empty tokens. Mirrors TString::Tokenize(). + * + * @param str input string to be tokenized + * @param delimiters set of delimiter characters + * @return vector of tokens (without the delimiters) + */ + static std::vector Split(const std::string &str, const std::string &delimiters); + + /** + *

Returns true if the string is a non-empty sequence of decimal + * digits only. Mirrors TString::IsDigit(). + * + * @param str string to be checked + * @return true if str consists of digits only + */ + static bool IsInt(const std::string &str); + + /** + *

Returns true if the string is a complete integer or floating point + * literal (optionally signed, with decimal point and/or exponent). + * Mirrors TString::IsFloat() for the relevant cases. + * + * @param str string to be checked + * @return true if str is a valid number + */ + static bool IsFloat(const std::string &str); + + /** + *

Converts the leading part of the string to an int (base 10). + * Mirrors TString::Atoi(). Returns 0 if no conversion is possible. + * + * @param str string to be converted + * @return converted integer value + */ + static int ToInt(const std::string &str); + + /** + *

Converts the leading part of the string to a double. + * Mirrors TString::Atof(). Returns 0.0 if no conversion is possible. + * + * @param str string to be converted + * @return converted double value + */ + static double ToDouble(const std::string &str); + + /** + *

Case-insensitive full-string equality. + * Mirrors TString::CompareTo(..., TString::kIgnoreCase) == 0. + * + * @param a first string + * @param b second string + * @return true if a and b are equal ignoring case + */ + static bool IsEqualNoCase(const std::string &a, const std::string &b); + + /** + *

Case-insensitive substring search. + * Mirrors TString::Contains(..., TString::kIgnoreCase). + * + * @param haystack string to be searched in + * @param needle substring to be searched for + * @return true if needle is contained in haystack ignoring case + */ + static bool ContainsNoCase(const std::string &haystack, const std::string &needle); + + /** + *

Case-insensitive prefix test. + * Mirrors TString::BeginsWith(..., TString::kIgnoreCase). + * + * @param str string to be tested + * @param prefix prefix to be searched for + * @return true if str starts with prefix ignoring case + */ + static bool BeginsWithNoCase(const std::string &str, const std::string &prefix); +}; + +#endif // _PSTRINGUTILS_H_ From 4319b4ad696caec78d957f36587b97a4caf7b8eb Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 11:54:33 +0200 Subject: [PATCH 02/24] PStringUtils::ToInt: signal conversion errors via from_chars ToInt() previously wrapped strtol() with a nullptr endptr, so a failed conversion was indistinguishable from a legitimate 0 (matching the old TString::Atoi() behaviour). Switch the implementation to std::from_chars and add an optional `bool *ok` out-parameter that reports success: it is set to false on a non-numeric string or an out-of-range value, true otherwise. Leading whitespace is skipped and trailing characters are ignored, preserving the Atoi-like prefix semantics. The parameter defaults to nullptr, so existing call sites keep compiling unchanged. Convert the parse-validation call sites in PMsrHandler to the single -parse ToInt(token, &ok) form, replacing the IsInt() guard + separate ToInt() (which parsed every token twice). All downstream >0 / >=0 / range / enum checks are preserved. Left untouched the call sites where IsInt() acts as a structural discriminator rather than a numeric validator (write path, xy-data index-vs-label, fParamInUse usage scans) and the IsFloat-guarded ToInt offsets, where switching to ToInt(&ok) would change parsing semantics. All 85 integration tests pass. Co-Authored-By: Claude Opus 4.8 --- src/classes/PMsrHandler.cpp | 243 +++++++++++++++-------------------- src/classes/PStringUtils.cpp | 27 +++- src/include/PStringUtils.h | 10 +- 3 files changed, 135 insertions(+), 145 deletions(-) diff --git a/src/classes/PMsrHandler.cpp b/src/classes/PMsrHandler.cpp index 7db1597e..0d632626 100644 --- a/src/classes/PMsrHandler.cpp +++ b/src/classes/PMsrHandler.cpp @@ -2865,9 +2865,9 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) error = true; } else { // handle the first 4 parameter since they are always the same // parameter number - if (PStringUtils::IsInt(tokens[0])) - param.fNo = PStringUtils::ToInt(tokens[0]); - else + bool ok = false; + param.fNo = PStringUtils::ToInt(tokens[0], &ok); + if (!ok) error = true; // parameter name @@ -3107,19 +3107,16 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - Int_t fittype = PStringUtils::ToInt(tokens[1]); - if ((fittype == MSR_FITTYPE_SINGLE_HISTO) || - (fittype == MSR_FITTYPE_SINGLE_HISTO_RRF) || - (fittype == MSR_FITTYPE_ASYM) || - (fittype == MSR_FITTYPE_ASYM_RRF) || - (fittype == MSR_FITTYPE_MU_MINUS) || - (fittype == MSR_FITTYPE_BNMR) || - (fittype == MSR_FITTYPE_NON_MUSR)) { - global.SetFitType(fittype); - } else { - error = true; - } + bool ok = false; + Int_t fittype = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ((fittype == MSR_FITTYPE_SINGLE_HISTO) || + (fittype == MSR_FITTYPE_SINGLE_HISTO_RRF) || + (fittype == MSR_FITTYPE_ASYM) || + (fittype == MSR_FITTYPE_ASYM_RRF) || + (fittype == MSR_FITTYPE_MU_MINUS) || + (fittype == MSR_FITTYPE_BNMR) || + (fittype == MSR_FITTYPE_NON_MUSR))) { + global.SetFitType(fittype); } else { error = true; } @@ -3143,13 +3140,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); - if (ival > 0) { - global.SetRRFPacking(ival); - } else { - error = true; - } + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ival > 0) { + global.SetRRFPacking(ival); } else { error = true; } @@ -3170,13 +3164,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i= 0) { - global.SetDataRange(ival, i-1); - } else { - error = true; - } + bool ok = false; + ival = PStringUtils::ToInt(tokens[i], &ok); + if (ok && ival >= 0) { + global.SetDataRange(ival, i-1); } else { error = true; } @@ -3261,13 +3252,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); - if (ival >= 0) { - global.SetPacking(ival); - } else { - error = true; - } + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ival >= 0) { + global.SetPacking(ival); } else { error = true; } @@ -3432,19 +3420,16 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - Int_t fittype = PStringUtils::ToInt(tokens[1]); - if ((fittype == MSR_FITTYPE_SINGLE_HISTO) || - (fittype == MSR_FITTYPE_SINGLE_HISTO_RRF) || - (fittype == MSR_FITTYPE_ASYM) || - (fittype == MSR_FITTYPE_ASYM_RRF) || - (fittype == MSR_FITTYPE_MU_MINUS) || - (fittype == MSR_FITTYPE_BNMR) || - (fittype == MSR_FITTYPE_NON_MUSR)) { - param.SetFitType(fittype); - } else { - error = true; - } + bool ok = false; + Int_t fittype = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ((fittype == MSR_FITTYPE_SINGLE_HISTO) || + (fittype == MSR_FITTYPE_SINGLE_HISTO_RRF) || + (fittype == MSR_FITTYPE_ASYM) || + (fittype == MSR_FITTYPE_ASYM_RRF) || + (fittype == MSR_FITTYPE_MU_MINUS) || + (fittype == MSR_FITTYPE_BNMR) || + (fittype == MSR_FITTYPE_NON_MUSR))) { + param.SetFitType(fittype); } else { error = true; } @@ -3459,8 +3444,9 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok) { if (ival > 0) param.SetAlphaParamNo(ival); else @@ -3485,8 +3471,9 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok) { if (ival > 0) param.SetBetaParamNo(ival); else @@ -3511,8 +3498,10 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - param.SetNormParamNo(PStringUtils::ToInt(tokens[1])); + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok) { + param.SetNormParamNo(ival); } else if (tokens[1].find("fun") != std::string::npos) { Int_t no; if (FilterNumber(tokens[1].c_str(), "fun", MSR_PARAM_FUN_OFFSET, no)) @@ -3533,15 +3522,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); - if (ival > 0) - param.SetBkgFitParamNo(ival); - else - error = true; - } else { + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ival > 0) + param.SetBkgFitParamNo(ival); + else error = true; - } } } @@ -3553,15 +3539,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); - if (ival > 0) - param.SetLifetimeParamNo(ival); - else - error = true; - } else { + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ival > 0) + param.SetLifetimeParamNo(ival); + else error = true; - } } } @@ -3579,15 +3562,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) runLinePresent = false; // this is needed to make sure that a run line is present before and ADDRUN is following for (UInt_t i=1; i= 0) - param.SetMap(ival); - else - error = true; - } else { + bool ok = false; + ival = PStringUtils::ToInt(tokens[i], &ok); + if (ok && ival >= 0) + param.SetMap(ival); + else error = true; - } } // check map entries, i.e. if the map values are within parameter bounds if (!fFourierOnly) { @@ -3678,15 +3658,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i 0) - param.SetBkgRange(ival, i-1); - else - error = true; - } else { + bool ok = false; + ival = PStringUtils::ToInt(tokens[i], &ok); + if (ok && ival > 0) + param.SetBkgRange(ival, i-1); + else error = true; - } } } } @@ -3700,15 +3677,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i 0) - param.SetDataRange(ival, i-1); - else - error = true; - } else { + bool ok = false; + ival = PStringUtils::ToInt(tokens[i], &ok); + if (ok && ival > 0) + param.SetDataRange(ival, i-1); + else error = true; - } } } } @@ -3816,15 +3790,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) if (tokens.size() != 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); - if (ival > 0) - param.SetPacking(ival); - else - error = true; - } else { + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok && ival > 0) + param.SetPacking(ival); + else error = true; - } } } @@ -4135,10 +4106,12 @@ Bool_t PMsrHandler::ParseFourierPhaseParVector(PMsrFourierStructure &fourier, co rmNoOf++; } sstr = sstr.substr(rmNoOf); // remove 'par' of 'parR' part. Rest should be an integer - if (PStringUtils::IsInt(sstr)) { + bool ok = false; + Int_t val = PStringUtils::ToInt(sstr, &ok); + if (ok) { if (rmNoOf == 4) // parR - fourier.fPhaseRef = PStringUtils::ToInt(sstr); - fourier.fPhaseParamNo.push_back(PStringUtils::ToInt(sstr)); + fourier.fPhaseRef = val; + fourier.fPhaseParamNo.push_back(val); } else { fLastErrorMsg.str(""); fLastErrorMsg.clear(); @@ -4222,9 +4195,9 @@ Bool_t PMsrHandler::ParseFourierPhaseParIterVector(PMsrFourierStructure &fourier Int_t x0, offset, noParam; // get X0 - if (PStringUtils::IsInt(tok[0])) { - x0 = PStringUtils::ToInt(tok[0]); - } else { + bool ok = false; + x0 = PStringUtils::ToInt(tok[0], &ok); + if (!ok) { fLastErrorMsg.str(""); fLastErrorMsg.clear(); fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** X0='" << tok[0] << "' is not an integer.\n"; @@ -4234,9 +4207,8 @@ Bool_t PMsrHandler::ParseFourierPhaseParIterVector(PMsrFourierStructure &fourier } // get offset - if (PStringUtils::IsInt(tok[1])) { - offset = PStringUtils::ToInt(tok[1]); - } else { + offset = PStringUtils::ToInt(tok[1], &ok); + if (!ok) { fLastErrorMsg.str(""); fLastErrorMsg.clear(); fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** offset='" << tok[1] << "' is not an integer.\n"; @@ -4246,9 +4218,8 @@ Bool_t PMsrHandler::ParseFourierPhaseParIterVector(PMsrFourierStructure &fourier } // get noParam - if (PStringUtils::IsInt(tok[2])) { - noParam = PStringUtils::ToInt(tok[2]); - } else { + noParam = PStringUtils::ToInt(tok[2], &ok); + if (!ok) { fLastErrorMsg.str(""); fLastErrorMsg.clear(); fLastErrorMsg << ">> PMsrHandler::ParseFourierPhaseParIterVector: **ERROR** #Param='" << tok[2] << "' is not an integer.\n"; @@ -4329,15 +4300,11 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) error = true; continue; } else { - if (PStringUtils::IsInt(tokens[1])) { - ival = PStringUtils::ToInt(tokens[1]); - if ((ival >= 0) && (ival <= 20)) { - fourier.fFourierPower = ival; - } else { // fourier power out of range - error = true; - continue; - } - } else { // fourier power not a number + bool ok = false; + ival = PStringUtils::ToInt(tokens[1], &ok); + if (ok && (ival >= 0) && (ival <= 20)) { + fourier.fFourierPower = ival; + } else { // fourier power not a number or out of range error = true; continue; } @@ -4616,9 +4583,9 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) if (tokens.size() < 2) { // plot type missing error = true; } else { - if (PStringUtils::IsInt(tokens[1])) - param.fPlotType = PStringUtils::ToInt(tokens[1]); - else + bool ok = false; + param.fPlotType = PStringUtils::ToInt(tokens[1], &ok); + if (!ok) error = true; } } else if (line.Contains("lifetimecorrection", TString::kIgnoreCase)) { @@ -4779,15 +4746,12 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) if (tokens.size() != 2) { error = true; } else { - if (PStringUtils::IsInt(tokens[1])) { - Int_t val = PStringUtils::ToInt(tokens[1]); - if (val > 0) - param.fViewPacking = val; - else - error = true; - } else { + bool ok = false; + Int_t val = PStringUtils::ToInt(tokens[1], &ok); + if (ok && val > 0) + param.fViewPacking = val; + else error = true; - } } } else if (iter1->fLine.Contains("rrf_freq", TString::kIgnoreCase)) { // expected entry: rrf_freq value unit @@ -4852,11 +4816,10 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) error = true; } else { // get rrf packing - if (PStringUtils::IsInt(tokens[1])) { - param.fRRFPacking = PStringUtils::ToInt(tokens[1]); - } else { + bool ok = false; + param.fRRFPacking = PStringUtils::ToInt(tokens[1], &ok); + if (!ok) error = true; - } } } else { error = true; diff --git a/src/classes/PStringUtils.cpp b/src/classes/PStringUtils.cpp index 573f7e66..59c05cb8 100644 --- a/src/classes/PStringUtils.cpp +++ b/src/classes/PStringUtils.cpp @@ -28,6 +28,7 @@ ***************************************************************************/ #include +#include #include #include "PStringUtils.h" @@ -125,12 +126,32 @@ bool PStringUtils::IsFloat(const std::string &str) *

Converts the leading part of the string to an int (base 10), mirroring * TString::Atoi(). Returns 0 if no conversion is possible. * + *

Uses std::from_chars so that conversion errors can be reported through + * the optional \a ok out-parameter: it is set to true when a valid integer + * was parsed (leading whitespace is skipped) and to false otherwise (no + * digits present, or the value is out of int range). Trailing non-numeric + * characters are ignored, as with TString::Atoi(). A null \a ok preserves the + * historic fire-and-forget behaviour. + * * \param str string to be converted - * \return converted integer value + * \param ok optional out-parameter signalling conversion success + * \return converted integer value (0 on error) */ -int PStringUtils::ToInt(const std::string &str) +int PStringUtils::ToInt(const std::string &str, bool *ok) { - return static_cast(std::strtol(str.c_str(), nullptr, 10)); + // mirror TString::Atoi(): skip leading whitespace, then parse the leading + // integer (a possible sign followed by decimal digits). + const char *begin = str.c_str(); + const char *end = begin + str.size(); + while (begin != end && std::isspace(static_cast(*begin))) + ++begin; + + int value = 0; + const std::from_chars_result res = std::from_chars(begin, end, value); + if (ok != nullptr) + *ok = (res.ec == std::errc{}); + + return value; } //-------------------------------------------------------------------------- diff --git a/src/include/PStringUtils.h b/src/include/PStringUtils.h index 8bd71cc0..3cc086c5 100644 --- a/src/include/PStringUtils.h +++ b/src/include/PStringUtils.h @@ -89,10 +89,16 @@ class PStringUtils *

Converts the leading part of the string to an int (base 10). * Mirrors TString::Atoi(). Returns 0 if no conversion is possible. * + *

If \a ok is non-null it is set to true when a valid integer was + * parsed and to false on error (no digits, or value out of int range). + * This allows callers to distinguish a legitimate 0 from a failed + * conversion, which the bare return value cannot express. + * * @param str string to be converted - * @return converted integer value + * @param ok optional out-parameter signalling conversion success + * @return converted integer value (0 on error) */ - static int ToInt(const std::string &str); + static int ToInt(const std::string &str, bool *ok = nullptr); /** *

Converts the leading part of the string to a double. From bb324260056905fce2205361063281106890c09c Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 12:08:01 +0200 Subject: [PATCH 03/24] PStringUtils::ToDouble: signal conversion errors like ToInt ToDouble() had the same shortcoming as ToInt(): wrapping strtod() with a nullptr endptr made a failed conversion indistinguishable from a legitimate 0.0. Add an optional `bool *ok` out-parameter that reports success. strtod() (with endptr + errno) is kept instead of std::from_chars so the accepted input set stays identical to TString::Atof() (leading whitespace skipped, leading '+' honoured, trailing characters ignored); ok is set false on a non-numeric string or an ERANGE overflow. The parameter defaults to nullptr, so existing call sites keep compiling unchanged. Convert the IsFloat-guarded ToDouble call sites in PMsrHandler to the single-parse ToDouble(token, &ok) form (replacing the IsFloat() guard + separate ToDouble() that parsed every token twice). All downstream >=0 / <=0 / range checks are preserved, and push_back sites only append on success so no spurious 0.0 is stored on error. Number-vs-keyword discriminators (pos.error/boundary "none", rrf_phase/fourier-phase parX) are restructured so the keyword branch is taken when ok is false. As a side effect this fixes a latent gap in the GLOBAL rrf_freq handler, where a non-numeric frequency previously slipped through with a stale value instead of raising an error. The IsFloat-guarded ToInt fit-range offsets (fgb/lgb) are intentionally left untouched, as there the guard type differs from the conversion. All 85 integration tests pass. Co-Authored-By: Claude Opus 4.8 --- src/classes/PMsrHandler.cpp | 243 ++++++++++++++++++----------------- src/classes/PStringUtils.cpp | 24 +++- src/include/PStringUtils.h | 9 +- 3 files changed, 151 insertions(+), 125 deletions(-) diff --git a/src/classes/PMsrHandler.cpp b/src/classes/PMsrHandler.cpp index 0d632626..50c71a9d 100644 --- a/src/classes/PMsrHandler.cpp +++ b/src/classes/PMsrHandler.cpp @@ -2874,15 +2874,13 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) param.fName = tokens[1].c_str(); // parameter value - if (PStringUtils::IsFloat(tokens[2])) - param.fValue = PStringUtils::ToDouble(tokens[2]); - else + param.fValue = PStringUtils::ToDouble(tokens[2], &ok); + if (!ok) error = true; - // parameter value - if (PStringUtils::IsFloat(tokens[3])) - param.fStep = PStringUtils::ToDouble(tokens[3]); - else + // parameter step + param.fStep = PStringUtils::ToDouble(tokens[3], &ok); + if (!ok) error = true; // 4 values, i.e. No Name Value Step @@ -2895,14 +2893,13 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) param.fNoOfParams = 5; // positive error - if (PStringUtils::IsFloat(tokens[4])) { + param.fPosError = PStringUtils::ToDouble(tokens[4], &ok); + if (ok) { param.fPosErrorPresent = true; - param.fPosError = PStringUtils::ToDouble(tokens[4]); + } else if (PStringUtils::IsEqualNoCase(tokens[4], "none")) { + param.fPosErrorPresent = false; } else { - if (PStringUtils::IsEqualNoCase(tokens[4], "none")) - param.fPosErrorPresent = false; - else - error = true; + error = true; } } @@ -2911,14 +2908,13 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) param.fNoOfParams = 7; // positive error - if (PStringUtils::IsFloat(tokens[4])) { + param.fPosError = PStringUtils::ToDouble(tokens[4], &ok); + if (ok) { param.fPosErrorPresent = true; - param.fPosError = PStringUtils::ToDouble(tokens[4]); + } else if (PStringUtils::IsEqualNoCase(tokens[4], "none")) { + param.fPosErrorPresent = false; } else { - if (PStringUtils::IsEqualNoCase(tokens[4], "none")) - param.fPosErrorPresent = false; - else - error = true; + error = true; } // lower boundary @@ -2926,8 +2922,8 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) if (PStringUtils::IsEqualNoCase(tokens[5], "none")) { // none param.fLowerBoundaryPresent = false; } else { // assuming that the lower boundary is a number - if (PStringUtils::IsFloat(tokens[5])) { - param.fLowerBoundary = PStringUtils::ToDouble(tokens[5]); + param.fLowerBoundary = PStringUtils::ToDouble(tokens[5], &ok); + if (ok) { param.fLowerBoundaryPresent = true; } else { error = true; @@ -2939,8 +2935,8 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines) if (PStringUtils::IsEqualNoCase(tokens[6], "none")) { // none param.fUpperBoundaryPresent = false; } else { // assuming a number - if (PStringUtils::IsFloat(tokens[6])) { - param.fUpperBoundary = PStringUtils::ToDouble(tokens[6]); + param.fUpperBoundary = PStringUtils::ToDouble(tokens[6], &ok); + if (ok) { param.fUpperBoundaryPresent = true; } else { error = true; @@ -3125,11 +3121,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) if (tokens.size() < 3) { error = true; } else { - if (PStringUtils::IsFloat(tokens[1])) { - dval = PStringUtils::ToDouble(tokens[1]); - if (dval <= 0.0) - error = true; - } + bool ok = false; + dval = PStringUtils::ToDouble(tokens[1], &ok); + if (!ok || dval <= 0.0) + error = true; if (!error) { global.SetRRFFreq(dval, tokens[2].c_str()); if (global.GetRRFFreq(tokens[2].c_str()) == RRF_FREQ_UNDEF) @@ -3152,12 +3147,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) if (tokens.size() < 2) { error = true; } else { - if (PStringUtils::IsFloat(tokens[1])) { - dval = PStringUtils::ToDouble(tokens[1]); + bool ok = false; + dval = PStringUtils::ToDouble(tokens[1], &ok); + if (ok) global.SetRRFPhase(dval); - } else { + else error = true; - } } } else if (iter->fLine.BeginsWith("data", TString::kIgnoreCase)) { // data if (tokens.size() < 3) { @@ -3178,15 +3173,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i= 0.0) - global.SetT0Bin(dval); - else - error = true; - } else { + bool ok = false; + dval = PStringUtils::ToDouble(tokens[i], &ok); + if (ok && dval >= 0.0) + global.SetT0Bin(dval); + else error = true; - } } } } else if (iter->fLine.BeginsWith("addt0", TString::kIgnoreCase)) { // addt0 @@ -3194,15 +3186,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i= 0.0) - global.SetAddT0Bin(dval, addT0Counter, i-1); - else - error = true; - } else { + bool ok = false; + dval = PStringUtils::ToDouble(tokens[i], &ok); + if (ok && dval >= 0.0) + global.SetAddT0Bin(dval, addT0Counter, i-1); + else error = true; - } } } addT0Counter++; @@ -3241,8 +3230,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines) global.SetFitRangeInBins(true); } else { // fit given in time, i.e. fit , where , are given as doubles for (UInt_t i=1; i<3; i++) { - if (PStringUtils::IsFloat(tokens[i])) - global.SetFitRange(PStringUtils::ToDouble(tokens[i]), i-1); + bool ok = false; + const double range = PStringUtils::ToDouble(tokens[i], &ok); + if (ok) + global.SetFitRange(range, i-1); else error = true; } @@ -3641,8 +3632,10 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i= 0.0) - param.SetT0Bin(dval); - else - error = true; - } else { + bool ok = false; + dval = PStringUtils::ToDouble(tokens[i], &ok); + if (ok && dval >= 0.0) + param.SetT0Bin(dval); + else error = true; - } } } } @@ -3718,15 +3708,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) error = true; } else { for (UInt_t i=1; i= 0.0) - param.SetAddT0Bin(dval, addT0Counter, i-1); - else - error = true; - } else { + bool ok = false; + dval = PStringUtils::ToDouble(tokens[i], &ok); + if (ok && dval >= 0.0) + param.SetAddT0Bin(dval, addT0Counter, i-1); + else error = true; - } } } @@ -3773,8 +3760,10 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines) param.SetFitRangeInBins(true); } else { // fit given in time, i.e. fit , where , are given as doubles for (UInt_t i=1; i<3; i++) { - if (PStringUtils::IsFloat(tokens[i])) - param.SetFitRange(PStringUtils::ToDouble(tokens[i]), i-1); + bool ok = false; + const double range = PStringUtils::ToDouble(tokens[i], &ok); + if (ok) + param.SetFitRange(range, i-1); else error = true; } @@ -4017,8 +4006,10 @@ Bool_t PMsrHandler::ParseFourierPhaseValueVector(PMsrFourierStructure &fourier, // convert all acceptable tokens for (UInt_t i=1; i1) { // make sure that no 'phase val, parX' mixture is present @@ -4440,9 +4431,9 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) continue; } else { for (UInt_t i=0; i<2; i++) { - if (PStringUtils::IsFloat(tokens[i+1])) { - fourier.fPlotRange[i] = PStringUtils::ToDouble(tokens[i+1]); - } else { + bool ok = false; + fourier.fPlotRange[i] = PStringUtils::ToDouble(tokens[i+1], &ok); + if (!ok) { error = true; continue; } @@ -4473,11 +4464,10 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines) break; case 3: for (UInt_t i=0; i<2; i++) { - if (PStringUtils::IsFloat(tokens[i+1])) { - fourier.fRangeForPhaseCorrection[i] = PStringUtils::ToDouble(tokens[i+1]); - } else { + bool ok = false; + fourier.fRangeForPhaseCorrection[i] = PStringUtils::ToDouble(tokens[i+1], &ok); + if (!ok) error = true; - } } break; default: @@ -4640,28 +4630,33 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) } else { // handle t_min - if (PStringUtils::IsFloat(tokens[1])) - param.fTmin.push_back(PStringUtils::ToDouble(tokens[1])); + bool ok = false; + const double tmin = PStringUtils::ToDouble(tokens[1], &ok); + if (ok) + param.fTmin.push_back(tmin); else error = true; // handle t_max - if (PStringUtils::IsFloat(tokens[2])) - param.fTmax.push_back(PStringUtils::ToDouble(tokens[2])); + const double tmax = PStringUtils::ToDouble(tokens[2], &ok); + if (ok) + param.fTmax.push_back(tmax); else error = true; if (tokens.size() == 5) { // y-axis interval given as well // handle y_min - if (PStringUtils::IsFloat(tokens[3])) - param.fYmin.push_back(PStringUtils::ToDouble(tokens[3])); + const double ymin = PStringUtils::ToDouble(tokens[3], &ok); + if (ok) + param.fYmin.push_back(ymin); else error = true; // handle y_max - if (PStringUtils::IsFloat(tokens[4])) - param.fYmax.push_back(PStringUtils::ToDouble(tokens[4])); + const double ymax = PStringUtils::ToDouble(tokens[4], &ok); + if (ok) + param.fYmax.push_back(ymax); else error = true; } @@ -4681,14 +4676,17 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines) for (UInt_t i=0; i(fParam.size()) < no) { - error = true; - } else { - // keep the parameter number in case parX was used - param.fRRFPhaseParamNo = no; - // get parameter value - param.fRRFPhase = fParam[no-1].fValue; - } + bool ok = false; + const double rrfPhase = PStringUtils::ToDouble(tokens[1], &ok); + if (ok) { + param.fRRFPhase = rrfPhase; + } else if (PStringUtils::BeginsWithNoCase(tokens[1], "par")) { // parameter value + Int_t no = 0; + if (FilterNumber(tokens[1].c_str(), "par", 0, no)) { + // check that the parameter is in range + if (static_cast(fParam.size()) < no) { + error = true; + } else { + // keep the parameter number in case parX was used + param.fRRFPhaseParamNo = no; + // get parameter value + param.fRRFPhase = fParam[no-1].fValue; } - } else { - error = true; } + } else { + error = true; } } } else if (iter1->fLine.Contains("rrf_packing", TString::kIgnoreCase)) { diff --git a/src/classes/PStringUtils.cpp b/src/classes/PStringUtils.cpp index 59c05cb8..45eaf89a 100644 --- a/src/classes/PStringUtils.cpp +++ b/src/classes/PStringUtils.cpp @@ -28,6 +28,7 @@ ***************************************************************************/ #include +#include #include #include @@ -161,12 +162,29 @@ int PStringUtils::ToInt(const std::string &str, bool *ok) *

Converts the leading part of the string to a double, mirroring * TString::Atof(). Returns 0.0 if no conversion is possible. * + *

Like ToInt(), conversion errors can be reported through the optional + * \a ok out-parameter: it is set to true when a value was parsed and to + * false otherwise (no number present, or the value is out of range). This + * lets callers distinguish a legitimate 0.0 from a failed conversion. + * strtod() (rather than std::from_chars) is used so that the accepted input + * set stays identical to TString::Atof() (leading whitespace is skipped, a + * leading '+' is honoured, trailing characters are ignored). A null \a ok + * preserves the historic fire-and-forget behaviour. + * * \param str string to be converted - * \return converted double value + * \param ok optional out-parameter signalling conversion success + * \return converted double value (0.0 on error) */ -double PStringUtils::ToDouble(const std::string &str) +double PStringUtils::ToDouble(const std::string &str, bool *ok) { - return std::strtod(str.c_str(), nullptr); + const char *begin = str.c_str(); + char *end = nullptr; + errno = 0; + const double value = std::strtod(begin, &end); + if (ok != nullptr) + *ok = (end != begin) && (errno != ERANGE); + + return value; } //-------------------------------------------------------------------------- diff --git a/src/include/PStringUtils.h b/src/include/PStringUtils.h index 3cc086c5..c28aadc6 100644 --- a/src/include/PStringUtils.h +++ b/src/include/PStringUtils.h @@ -104,10 +104,15 @@ class PStringUtils *

Converts the leading part of the string to a double. * Mirrors TString::Atof(). Returns 0.0 if no conversion is possible. * + *

If \a ok is non-null it is set to true when a value was parsed and + * to false on error (no number, or value out of range), allowing callers + * to distinguish a legitimate 0.0 from a failed conversion. + * * @param str string to be converted - * @return converted double value + * @param ok optional out-parameter signalling conversion success + * @return converted double value (0.0 on error) */ - static double ToDouble(const std::string &str); + static double ToDouble(const std::string &str, bool *ok = nullptr); /** *

Case-insensitive full-string equality. From aa5cdf8d6a67747b8bfd37a1cc26cf391e2a280c Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 14:08:25 +0200 Subject: [PATCH 04/24] add strToNum test driver for PStringUtils Stand-alone test under src/tests/strToNum exercising every PStringUtils method (Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase / ContainsNoCase / BeginsWithNoCase). Three modes: built-in pass/fail suite, ad-hoc inspection of command-line strings, and an interactive prompt. Adds -h/--help and -i/--interactive options. Pure C++17, no ROOT dependency. Co-Authored-By: Claude Opus 4.8 --- src/tests/strToNum/CMakeLists.txt | 37 ++++ src/tests/strToNum/strToNum.cpp | 300 ++++++++++++++++++++++++++++++ 2 files changed, 337 insertions(+) create mode 100644 src/tests/strToNum/CMakeLists.txt create mode 100644 src/tests/strToNum/strToNum.cpp diff --git a/src/tests/strToNum/CMakeLists.txt b/src/tests/strToNum/CMakeLists.txt new file mode 100644 index 00000000..613bb0cc --- /dev/null +++ b/src/tests/strToNum/CMakeLists.txt @@ -0,0 +1,37 @@ +#------------------------------------------------------ +# CMakeLists.txt for strToNum +# +# little stand-alone test driver for the dependency-free +# PStringUtils class (pure C++17, no ROOT needed). +# +# build (stand-alone): +# cmake -S . -B build +# cmake --build build +# ./build/strToNum +# +# Andreas Suter, 2026/06/06 +#------------------------------------------------------ +cmake_minimum_required(VERSION 3.9) + +project(strToNum VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif (NOT CMAKE_BUILD_TYPE) + +#--- the class under test lives in src/classes, its header in src/include ----- +set(MUSRFIT_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../classes) +set(MUSRFIT_INC ${CMAKE_CURRENT_SOURCE_DIR}/../../include) + +add_executable(strToNum + strToNum.cpp + ${MUSRFIT_SRC}/PStringUtils.cpp +) + +target_include_directories(strToNum + PRIVATE ${MUSRFIT_INC} +) diff --git a/src/tests/strToNum/strToNum.cpp b/src/tests/strToNum/strToNum.cpp new file mode 100644 index 00000000..f06f53ac --- /dev/null +++ b/src/tests/strToNum/strToNum.cpp @@ -0,0 +1,300 @@ +/*************************************************************************** + + strToNum.cpp + + Author: Andreas Suter + e-mail: andreas.suter@psi.ch + + Little stand-alone test driver for the PStringUtils class. It exercises + Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase / + ContainsNoCase / BeginsWithNoCase and reports a pass/fail summary. + + Usage: + strToNum -> run the built-in test suite + strToNum -> show what PStringUtils makes of the given + string(s) on the command line (ad-hoc checks) + strToNum -i -> interactive mode: type a string at the prompt + and see the result of every PStringUtils method + (empty line or 'quit' to leave) + +***************************************************************************/ + +/*************************************************************************** + * Copyright (C) 2007-2026 by Andreas Suter * + * andreas.suter@psi.ch * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "PStringUtils.h" + +//-------------------------------------------------------------------------- +// tiny test bookkeeping +//-------------------------------------------------------------------------- +static int gPassed = 0; +static int gFailed = 0; + +static void check(const std::string &what, bool ok) +{ + if (ok) { + ++gPassed; + std::cout << " [ ok ] " << what << std::endl; + } else { + ++gFailed; + std::cout << " [FAIL] " << what << std::endl; + } +} + +//-------------------------------------------------------------------------- +static std::string vecToStr(const std::vector &v) +{ + std::string s = "{"; + for (std::vector::size_type i = 0; i < v.size(); ++i) { + s += "'" + v[i] + "'"; + if (i + 1 < v.size()) + s += ", "; + } + s += "}"; + return s; +} + +//-------------------------------------------------------------------------- +static void testSplit() +{ + std::cout << "Split:" << std::endl; + + std::vector r = PStringUtils::Split("alpha beta gamma", " "); + std::cout << " input: 'alpha beta gamma', delim: ' '" << std::endl; + check("3 tokens on single space delimiter", + r.size() == 3 && r[0] == "alpha" && r[1] == "beta" && r[2] == "gamma"); + std::cout << "----" << std::endl; + r = PStringUtils::Split(" , 1 , 2 ,, 3 ,", " ,"); + std::cout << " input: ' , 1 , 2 ,, 3 ,', delim: ' ,'" << std::endl; + std::cout << " -> "; + for (auto i: r) + std::cout << i << ", "; + std::cout << std::endl; + check("mixed/repeated delimiters, empty tokens skipped", + r.size() == 3 && r[0] == "1" && r[1] == "2" && r[2] == "3"); + std::cout << "----" << std::endl; + + r = PStringUtils::Split("", " "); + check("empty input -> no tokens", r.empty()); + + r = PStringUtils::Split(" ", " "); + check("delimiters only -> no tokens", r.empty()); + + r = PStringUtils::Split("nodelim", ",;"); + check("no delimiter present -> single token", + r.size() == 1 && r[0] == "nodelim"); +} + +//-------------------------------------------------------------------------- +static void testIsInt() +{ + std::cout << "IsInt:" << std::endl; + + check("'12345' is int", PStringUtils::IsInt("12345")); + check("' 42 ' (surrounding ws) is int", PStringUtils::IsInt(" 42 ")); + check("'' is not int", !PStringUtils::IsInt("")); + check("' ' (ws only) is not int", !PStringUtils::IsInt(" ")); + check("'-5' is not int (sign not allowed)", !PStringUtils::IsInt("-5")); + check("'3.14' is not int", !PStringUtils::IsInt("3.14")); + check("'12a' is not int", !PStringUtils::IsInt("12a")); +} + +//-------------------------------------------------------------------------- +static void testIsFloat() +{ + std::cout << "IsFloat:" << std::endl; + + check("'3.14' is float", PStringUtils::IsFloat("3.14")); + check("'-1.2e-3' is float", PStringUtils::IsFloat("-1.2e-3")); + check("'+42' is float", PStringUtils::IsFloat("+42")); + check("'.5' is float", PStringUtils::IsFloat(".5")); + check("' 6.022e23 ' (surrounding ws) is float", + PStringUtils::IsFloat(" 6.022e23 ")); + check("'' is not float", !PStringUtils::IsFloat("")); + check("'nan' is not float", !PStringUtils::IsFloat("nan")); + check("'inf' is not float", !PStringUtils::IsFloat("inf")); + check("'1.2.3' is not float", !PStringUtils::IsFloat("1.2.3")); + check("'12abc' is not float", !PStringUtils::IsFloat("12abc")); +} + +//-------------------------------------------------------------------------- +static void testToInt() +{ + std::cout << "ToInt:" << std::endl; + + bool ok = false; + check("'42' -> 42, ok", PStringUtils::ToInt("42", &ok) == 42 && ok); + check("' -7' -> -7, ok", PStringUtils::ToInt(" -7", &ok) == -7 && ok); + check("'123abc' -> 123, ok (trailing ignored)", + PStringUtils::ToInt("123abc", &ok) == 123 && ok); + check("'abc' -> 0, !ok", PStringUtils::ToInt("abc", &ok) == 0 && !ok); + check("'' -> 0, !ok", PStringUtils::ToInt("", &ok) == 0 && !ok); + check("'99999999999999999999' -> out of range, !ok", + (PStringUtils::ToInt("99999999999999999999", &ok), !ok)); + check("null ok pointer is tolerated", PStringUtils::ToInt("17") == 17); +} + +//-------------------------------------------------------------------------- +static void testToDouble() +{ + std::cout << "ToDouble:" << std::endl; + + bool ok = false; + check("'3.14' -> 3.14, ok", + std::fabs(PStringUtils::ToDouble("3.14", &ok) - 3.14) < 1e-12 && ok); + check("' +1.5e2' -> 150, ok", + std::fabs(PStringUtils::ToDouble(" +1.5e2", &ok) - 150.0) < 1e-9 && ok); + check("'2.5xyz' -> 2.5, ok (trailing ignored)", + std::fabs(PStringUtils::ToDouble("2.5xyz", &ok) - 2.5) < 1e-12 && ok); + check("'abc' -> 0.0, !ok", PStringUtils::ToDouble("abc", &ok) == 0.0 && !ok); + check("'' -> 0.0, !ok", PStringUtils::ToDouble("", &ok) == 0.0 && !ok); + check("'1e400' -> out of range, !ok", + (PStringUtils::ToDouble("1e400", &ok), !ok)); + check("null ok pointer is tolerated", + std::fabs(PStringUtils::ToDouble("0.25") - 0.25) < 1e-12); +} + +//-------------------------------------------------------------------------- +static void testCaseHelpers() +{ + std::cout << "IsEqualNoCase / ContainsNoCase / BeginsWithNoCase:" << std::endl; + + check("'Fit' == 'fIT' (no case)", PStringUtils::IsEqualNoCase("Fit", "fIT")); + check("'Fit' != 'Fits'", !PStringUtils::IsEqualNoCase("Fit", "Fits")); + check("'' == ''", PStringUtils::IsEqualNoCase("", "")); + + check("'Hello World' contains 'LO WO' (no case)", + PStringUtils::ContainsNoCase("Hello World", "LO WO")); + check("empty needle is contained", + PStringUtils::ContainsNoCase("abc", "")); + check("needle longer than haystack is not contained", + !PStringUtils::ContainsNoCase("ab", "abc")); + + check("'THEORY' begins with 'the' (no case)", + PStringUtils::BeginsWithNoCase("THEORY", "the")); + check("'THEORY' does not begin with 'ory'", + !PStringUtils::BeginsWithNoCase("THEORY", "ory")); +} + +//-------------------------------------------------------------------------- +static void inspect(const std::string &str) +{ + std::cout << "Inspecting '" << str << "':" << std::endl; + std::cout << " Split(' \\t,;') = " + << vecToStr(PStringUtils::Split(str, " \t,;")) << std::endl; + std::cout << " IsInt = " << (PStringUtils::IsInt(str) ? "true" : "false") << std::endl; + std::cout << " IsFloat = " << (PStringUtils::IsFloat(str) ? "true" : "false") << std::endl; + + bool ok = false; + int i = PStringUtils::ToInt(str, &ok); + std::cout << " ToInt = " << i << " (ok=" << (ok ? "true" : "false") << ")" << std::endl; + double d = PStringUtils::ToDouble(str, &ok); + std::cout << " ToDouble = " << d << " (ok=" << (ok ? "true" : "false") << ")" << std::endl; +} + +//-------------------------------------------------------------------------- +static void usage(const char *prog) +{ + std::cout + << "usage: " << prog << " [options] [string ...]\n" + << "\n" + << "Little stand-alone test driver for the PStringUtils class. It exercises\n" + << "Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /\n" + << "ContainsNoCase / BeginsWithNoCase.\n" + << "\n" + << "options:\n" + << " -h, --help show this help and exit\n" + << " -i, --interactive interactive mode: type a string at the prompt and\n" + << " see the result of every PStringUtils method\n" + << " (empty line, 'quit'/'exit' or Ctrl-D leaves)\n" + << "\n" + << "arguments:\n" + << " string ... one or more strings to inspect; the result of every\n" + << " PStringUtils method is printed for each of them\n" + << "\n" + << "with no options and no arguments the built-in test suite is run; the exit\n" + << "code is 0 if all checks pass and 1 otherwise.\n" + << "\n" + << "examples:\n" + << " " << prog << " run the built-in test suite\n" + << " " << prog << " \" -42xyz\" 1.5e-3 inspect the given strings\n" + << " " << prog << " -i interactive mode\n"; +} + +//-------------------------------------------------------------------------- +static void interactive() +{ + std::cout << "==== PStringUtils interactive mode ====" << std::endl; + std::cout << "Enter a string to test the methods on it." << std::endl; + std::cout << "An empty line or 'quit' leaves." << std::endl << std::endl; + + std::string line; + while (true) { + std::cout << "strToNum> " << std::flush; + if (!std::getline(std::cin, line)) // EOF (e.g. Ctrl-D) + break; + if (line.empty() || line == "quit" || line == "exit") + break; + inspect(line); + std::cout << std::endl; + } +} + +//-------------------------------------------------------------------------- +int main(int argc, char *argv[]) +{ + if (argc > 1) { + const std::string arg1 = argv[1]; + if (arg1 == "-h" || arg1 == "--help") { + usage(argv[0]); + return 0; + } + if (arg1 == "-i" || arg1 == "--interactive") { + interactive(); + return 0; + } + // ad-hoc mode: inspect the strings given on the command line + for (int i = 1; i < argc; ++i) { + inspect(argv[i]); + std::cout << std::endl; + } + return 0; + } + + std::cout << "==== PStringUtils test suite ====" << std::endl << std::endl; + + testSplit(); + testIsInt(); + testIsFloat(); + testToInt(); + testToDouble(); + testCaseHelpers(); + + std::cout << std::endl + << "==== summary: " << gPassed << " passed, " + << gFailed << " failed ====" << std::endl; + + return (gFailed == 0) ? 0 : 1; +} From 07f9c744b35aab71bc18084317e06a745593d06e Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 14:14:11 +0200 Subject: [PATCH 05/24] PStringUtils::IsInt: accept an optional leading sign IsInt now recognises (possibly signed) integers such as "-5" or "+42", making it slightly more permissive than TString::IsDigit(). A lone sign, a double sign, or a sign following a digit are still rejected. strToNum test expectations updated accordingly. Co-Authored-By: Claude Opus 4.8 --- src/classes/PStringUtils.cpp | 27 +++++++++++++++++++-------- src/include/PStringUtils.h | 8 +++++--- src/tests/strToNum/strToNum.cpp | 6 +++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/classes/PStringUtils.cpp b/src/classes/PStringUtils.cpp index 45eaf89a..2a588c7a 100644 --- a/src/classes/PStringUtils.cpp +++ b/src/classes/PStringUtils.cpp @@ -65,23 +65,34 @@ std::vector PStringUtils::Split(const std::string &str, const std:: // IsInt (static) //-------------------------------------------------------------------------- /** - *

Returns true if the string is a non-empty sequence of decimal digits - * only. Mirrors the semantics of TString::IsDigit(). + *

Returns true if the string is an integer literal, i.e. a non-empty + * sequence of decimal digits with an optional single leading sign (+/-). + * This is slightly more permissive than TString::IsDigit(), which rejects a + * sign, so that negative/positive integers such as "-5" or "+42" are also + * recognised. * * \param str string to be checked - * \return true if str consists of digits only + * \return true if str is a (possibly signed) integer */ bool PStringUtils::IsInt(const std::string &str) { - // mirror TString::IsDigit(): all characters must be digits or whitespace, - // and there must be at least one digit (surrounding/embedded whitespace is - // tolerated, e.g. for tokens split on ',' or ';' only). + // all characters must be digits or whitespace, with an optional single + // leading sign (+/-) preceding the digits, and there must be at least one + // digit (surrounding/embedded whitespace is tolerated, e.g. for tokens + // split on ',' or ';' only). bool hasDigit = false; + bool hasSign = false; for (char c : str) { - if (std::isdigit(static_cast(c))) + if (std::isdigit(static_cast(c))) { hasDigit = true; - else if (!std::isspace(static_cast(c))) + } else if (c == '+' || c == '-') { + // a sign is only valid before any digit and may appear at most once + if (hasDigit || hasSign) + return false; + hasSign = true; + } else if (!std::isspace(static_cast(c))) { return false; + } } return hasDigit; } diff --git a/src/include/PStringUtils.h b/src/include/PStringUtils.h index c28aadc6..01eeb4f0 100644 --- a/src/include/PStringUtils.h +++ b/src/include/PStringUtils.h @@ -67,11 +67,13 @@ class PStringUtils static std::vector Split(const std::string &str, const std::string &delimiters); /** - *

Returns true if the string is a non-empty sequence of decimal - * digits only. Mirrors TString::IsDigit(). + *

Returns true if the string is a (possibly signed) integer literal, + * i.e. a non-empty sequence of decimal digits with an optional single + * leading sign (+/-). Slightly more permissive than TString::IsDigit(), + * which rejects a sign, so that e.g. "-5" is also recognised. * * @param str string to be checked - * @return true if str consists of digits only + * @return true if str is a (possibly signed) integer */ static bool IsInt(const std::string &str); diff --git a/src/tests/strToNum/strToNum.cpp b/src/tests/strToNum/strToNum.cpp index f06f53ac..993e029f 100644 --- a/src/tests/strToNum/strToNum.cpp +++ b/src/tests/strToNum/strToNum.cpp @@ -114,9 +114,13 @@ static void testIsInt() check("'12345' is int", PStringUtils::IsInt("12345")); check("' 42 ' (surrounding ws) is int", PStringUtils::IsInt(" 42 ")); + check("'-5' is int (negative)", PStringUtils::IsInt("-5")); + check("'+42' is int (positive sign)", PStringUtils::IsInt("+42")); check("'' is not int", !PStringUtils::IsInt("")); check("' ' (ws only) is not int", !PStringUtils::IsInt(" ")); - check("'-5' is not int (sign not allowed)", !PStringUtils::IsInt("-5")); + check("'-' (sign only) is not int", !PStringUtils::IsInt("-")); + check("'+-5' (double sign) is not int", !PStringUtils::IsInt("+-5")); + check("'5-3' (sign after digit) is not int", !PStringUtils::IsInt("5-3")); check("'3.14' is not int", !PStringUtils::IsInt("3.14")); check("'12a' is not int", !PStringUtils::IsInt("12a")); } From 1a85444763454a24b52ee8f098b0e8f58d5d6c93 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 14:32:17 +0200 Subject: [PATCH 06/24] PStartupHandler: replace TObjArray/TObjString with PStringUtils Replace the ROOT TString::Tokenize()/TObjArray/TObjString machinery used for parsing the RGB color code in OnCharacters() with the C++17 PStringUtils helpers (Split/IsInt/ToInt). Drops the manual heap cleanup and the / includes. Co-Authored-By: Claude Opus 4.8 --- src/classes/PStartupHandler.cpp | 95 +++++++++++++-------------------- 1 file changed, 38 insertions(+), 57 deletions(-) diff --git a/src/classes/PStartupHandler.cpp b/src/classes/PStartupHandler.cpp index 47bb48ca..7907e0ec 100644 --- a/src/classes/PStartupHandler.cpp +++ b/src/classes/PStartupHandler.cpp @@ -34,13 +34,12 @@ #include #include -#include -#include #include #include #include #include "PStartupHandler.h" +#include "PStringUtils.h" ClassImpQ(PStartupHandler) @@ -451,8 +450,6 @@ void PStartupHandler::OnEndElement(const Char_t *str) */ void PStartupHandler::OnCharacters(const Char_t *str) { - TObjArray *tokens; - TObjString *ostr; TString tstr; Int_t color, r, g, b, ival; @@ -481,60 +478,44 @@ void PStartupHandler::OnCharacters(const Char_t *str) } break; case eColor: - // check that str is a rbg code - tstr = TString(str); - tokens = tstr.Tokenize(","); - // check that there any tokens - if (!tokens) { - std::cerr << std::endl << "PStartupHandler **WARNING** '" << str << "' is not a rbg code, will ignore it"; - std::cerr << std::endl; - return; + { + // check that str is a rbg code + std::vector rgb = PStringUtils::Split(str, ","); + // check there is the right number of tokens + if (rgb.size() != 3) { + std::cerr << std::endl << "PStartupHandler **WARNING** '" << str << "' is not a rbg code, will ignore it"; + std::cerr << std::endl; + return; + } + // get r + if (PStringUtils::IsInt(rgb[0])) { + r = PStringUtils::ToInt(rgb[0]); + } else { + std::cerr << std::endl << "PStartupHandler **WARNING** r within the rgb code is not a number, will ignore it"; + std::cerr << std::endl; + return; + } + // get g + if (PStringUtils::IsInt(rgb[1])) { + g = PStringUtils::ToInt(rgb[1]); + } else { + std::cerr << std::endl << "PStartupHandler **WARNING** g within the rgb code is not a number, will ignore it"; + std::cerr << std::endl; + return; + } + // get b + if (PStringUtils::IsInt(rgb[2])) { + b = PStringUtils::ToInt(rgb[2]); + } else { + std::cerr << std::endl << "PStartupHandler **WARNING** b within the rgb code is not a number, will ignore it"; + std::cerr << std::endl; + return; + } + // generate the ROOT color code based on str + color = TColor::GetColor(r,g,b); + // add the color code to the color list + fColorList.push_back(color); } - // check there is the right number of tokens - if (tokens->GetEntries() != 3) { - std::cerr << std::endl << "PStartupHandler **WARNING** '" << str << "' is not a rbg code, will ignore it"; - std::cerr << std::endl; - return; - } - // get r - ostr = dynamic_cast(tokens->At(0)); - tstr = ostr->GetString(); - if (tstr.IsDigit()) { - r = tstr.Atoi(); - } else { - std::cerr << std::endl << "PStartupHandler **WARNING** r within the rgb code is not a number, will ignore it"; - std::cerr << std::endl; - return; - } - // get g - ostr = dynamic_cast(tokens->At(1)); - tstr = ostr->GetString(); - if (tstr.IsDigit()) { - g = tstr.Atoi(); - } else { - std::cerr << std::endl << "PStartupHandler **WARNING** g within the rgb code is not a number, will ignore it"; - std::cerr << std::endl; - return; - } - // get b - ostr = dynamic_cast(tokens->At(2)); - tstr = ostr->GetString(); - if (tstr.IsDigit()) { - b = tstr.Atoi(); - } else { - std::cerr << std::endl << "PStartupHandler **WARNING** b within the rgb code is not a number, will ignore it"; - std::cerr << std::endl; - return; - } - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } - // generate the ROOT color code based on str - color = TColor::GetColor(r,g,b); - // add the color code to the color list - fColorList.push_back(color); break; case eUnits: tstr = TString(str); From e3e84a6e56640ce0f4d6b6372b49140ccf9e39c0 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 14:37:35 +0200 Subject: [PATCH 07/24] PMsrHandler: escape '?' to avoid trigraph warning in date placeholder The fallback date placeholder string contained the sequence '??-', which the compiler interprets as a trigraph for '~' (-Wtrigraphs warning). Escape the question marks (\?) so the literal string is unchanged while the warning is silenced. Co-Authored-By: Claude Opus 4.8 --- src/classes/PMsrHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classes/PMsrHandler.cpp b/src/classes/PMsrHandler.cpp index 50c71a9d..4c8b3cf3 100644 --- a/src/classes/PMsrHandler.cpp +++ b/src/classes/PMsrHandler.cpp @@ -4994,7 +4994,7 @@ Bool_t PMsrHandler::HandleStatisticEntry(PMsrLines &lines) if (status == 2) { fStatistic.fDate = TString(date)+TString(", ")+TString(time); } else { - fStatistic.fDate = TString("????-??-??, ??:??:??"); + fStatistic.fDate = TString("\?\?\?\?-\?\?-\?\?, \?\?:\?\?:\?\?"); } } // extract chisq From b2db41194fd8127c3650a80d52eae52b72620d12 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 14:43:11 +0200 Subject: [PATCH 08/24] musredit: capture 'this' explicitly to silence C++20 [-Wdeprecated] The QProcess::finished lambdas used [=], which implicitly captures 'this' to call member functions (exitStatusMusrWiz, fileReload, exitStatusMusrSetSteps). Implicit 'this' capture via [=] is deprecated in C++20; name it explicitly with [=, this]. Applied to both the qt6 and qt5 copies. Co-Authored-By: Claude Opus 4.8 --- src/musredit_qt5/musredit/PTextEdit.cpp | 6 +++--- src/musredit_qt6/musredit/PTextEdit.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/musredit_qt5/musredit/PTextEdit.cpp b/src/musredit_qt5/musredit/PTextEdit.cpp index 7a5b3acf..533cbc67 100644 --- a/src/musredit_qt5/musredit/PTextEdit.cpp +++ b/src/musredit_qt5/musredit/PTextEdit.cpp @@ -2046,7 +2046,7 @@ void PTextEdit::musrWiz() // handle return status of musrWiz connect(proc, static_cast(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); }); + [=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); }); // make sure that the system environment variables are properly set QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); @@ -2761,7 +2761,7 @@ void PTextEdit::musrT0() proc->setWorkingDirectory(workDir); connect(proc, QOverload::of(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); }); + [=, this](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); }); proc->start(cmd, arg); if (!proc->waitForStarted()) { @@ -2921,7 +2921,7 @@ void PTextEdit::musrSetSteps() // handle return status of musrStep connect(proc, static_cast(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); }); + [=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); }); // make sure that the system environment variables are properly set QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); diff --git a/src/musredit_qt6/musredit/PTextEdit.cpp b/src/musredit_qt6/musredit/PTextEdit.cpp index 9e9f31af..d78343f3 100644 --- a/src/musredit_qt6/musredit/PTextEdit.cpp +++ b/src/musredit_qt6/musredit/PTextEdit.cpp @@ -2160,7 +2160,7 @@ void PTextEdit::musrWiz() // handle return status of musrWiz connect(proc, static_cast(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); }); + [=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); }); // make sure that the system environment variables are properly set QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); @@ -2868,7 +2868,7 @@ void PTextEdit::musrT0() proc->setWorkingDirectory(workDir); connect(proc, QOverload::of(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); }); + [=, this](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); }); proc->start(cmd, arg); if (!proc->waitForStarted()) { @@ -3022,7 +3022,7 @@ void PTextEdit::musrSetSteps() // handle return status of musrStep connect(proc, static_cast(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); }); + [=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); }); // make sure that the system environment variables are properly set QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); From 8cdbb2492999260cc54628648e7f6a3b963c7eb9 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 14:50:12 +0200 Subject: [PATCH 09/24] PRun*: use reduction(+:mllh) instead of deprecated reduction(-:mllh) OpenMP 5.2 deprecates the '-' reduction operator (-Wdeprecated-openmp) because it is functionally identical to '+': the private reduction copy is initialised to 0 and partial results are combined by addition in both cases. All affected loops accumulate with 'mllh += ...', so switching to reduction(+:mllh) is results-identical and silences the warning. Fixes the pragma in PRunMuMinus.cpp and the two in PRunSingleHisto.cpp, and corrects the now-inaccurate "for subtraction" doc comment. Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunMuMinus.cpp | 2 +- src/classes/PRunSingleHisto.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/classes/PRunMuMinus.cpp b/src/classes/PRunMuMinus.cpp index ca94366e..5440e154 100644 --- a/src/classes/PRunMuMinus.cpp +++ b/src/classes/PRunMuMinus.cpp @@ -383,7 +383,7 @@ Double_t PRunMuMinus::CalcMaxLikelihood(const std::vector& par) Int_t chunk = (fEndTimeBin - fStartTimeBin)/omp_get_num_procs(); if (chunk < 10) chunk = 10; - #pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(-:mllh) + #pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(+:mllh) #endif for (i=fStartTimeBin; i < fEndTimeBin; ++i) { time = fData.GetDataTimeStart() + static_cast(i)*fData.GetDataTimeStep(); diff --git a/src/classes/PRunSingleHisto.cpp b/src/classes/PRunSingleHisto.cpp index ebe601ea..1a44839c 100644 --- a/src/classes/PRunSingleHisto.cpp +++ b/src/classes/PRunSingleHisto.cpp @@ -439,7 +439,7 @@ Double_t PRunSingleHisto::CalcChiSquareExpected(const std::vector& par * OpenMP Parallelization: * - Dynamic scheduling with chunk size = (N_bins / N_processors), minimum 10 * - Private variables per thread: i, time, theo, data - * - Reduction performed on mllh sum (note: reduction(-:mllh) for subtraction) + * - Reduction performed on mllh sum (reduction(+:mllh)) * * When to Use Maximum Likelihood vs. χ²: * - Use likelihood: Low count rates (< 100 counts/bin), asymmetric errors @@ -515,7 +515,7 @@ Double_t PRunSingleHisto::CalcMaxLikelihood(const std::vector& par) Int_t chunk = (fEndTimeBin - fStartTimeBin)/omp_get_num_procs(); if (chunk < 10) chunk = 10; - #pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(-:mllh) + #pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(+:mllh) #endif for (i=fStartTimeBin; i(i)*fData.GetDataTimeStep(); @@ -650,7 +650,7 @@ Double_t PRunSingleHisto::CalcMaxLikelihoodExpected(const std::vector& Int_t chunk = (fEndTimeBin - fStartTimeBin)/omp_get_num_procs(); if (chunk < 10) chunk = 10; - #pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(-:mllh) + #pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(+:mllh) #endif for (i=fStartTimeBin; i(i)*fData.GetDataTimeStep(); From e7af0a781e6286b90c120a36f98f7f91220a4904 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 15:02:17 +0200 Subject: [PATCH 10/24] libCuba: guard memcpy in SobolIni against negative length GCC's -Wstringop-overflow flagged the memcpy in SobolIni() with a bound of (size_t)(-4): on the (in practice unreachable) path where the Sobol generator polynomial 'powers' is 0, the bit-count loop leaves inibits at its initial -1, so inibits*sizeof underflows. The generator-polynomial table always has a non-zero first column, so this never happens at run time, but the compiler cannot prove it. Guard the copy with 'if (inibits > 0)', which silences the false-positive warning and hardens the edge case without changing behaviour for valid input. Co-Authored-By: Claude Opus 4.8 --- src/external/libCuba/src/common/Random.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/external/libCuba/src/common/Random.c b/src/external/libCuba/src/common/Random.c index 6d606157..d1720c5d 100644 --- a/src/external/libCuba/src/common/Random.c +++ b/src/external/libCuba/src/common/Random.c @@ -102,7 +102,8 @@ static inline void SobolIni(This *t) int inibits = -1, bit; for( j = powers; j; j >>= 1 ) ++inibits; - memcpy(pv, pini, inibits*sizeof *pini); + if( inibits > 0 ) + memcpy(pv, pini, inibits*sizeof *pini); pini += 8; for( bit = inibits; bit <= nbits; ++bit ) { From 76070f20987fefa27a67459c7ccf3072781ac576 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 15:16:50 +0200 Subject: [PATCH 11/24] msr2msr: replace TObjArray/TObjString with PStringUtils Replace ROOT's TString::Tokenize() + TObjArray/TObjString token handling with the dependency-free PStringUtils::Split() across msr2msr_run, msr2msr_param and msr2msr_theory. Split() mirrors Tokenize() semantics (skips empty tokens), so token counts and indices are unchanged. Using a std::vector removes the manual TObjArray cleanup and incidentally fixes a pre-existing leak in msr2msr_theory, which tokenized in every branch but never deleted the TObjArray. Since msr2msr links only against ROOT (not PMusr, where PStringUtils lives) and PStringUtils is pure C++17, compile classes/PStringUtils.cpp directly into the msr2msr executable rather than pulling in the whole PMusr library. Co-Authored-By: Claude Opus 4.8 --- src/CMakeLists.txt | 2 +- src/msr2msr.cpp | 138 +++++++++++++++++---------------------------- 2 files changed, 53 insertions(+), 87 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0a6d836..4e2d2936 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,7 +75,7 @@ target_include_directories(msr2data ) target_link_libraries(msr2data ${ROOT_LIBRARIES} ${MUSRFIT_LIBS}) -add_executable(msr2msr msr2msr.cpp) +add_executable(msr2msr msr2msr.cpp classes/PStringUtils.cpp) target_link_libraries(msr2msr ${ROOT_LIBRARIES}) add_executable(musrfit musrfit.cpp) diff --git a/src/msr2msr.cpp b/src/msr2msr.cpp index d7f93e77..6852485c 100644 --- a/src/msr2msr.cpp +++ b/src/msr2msr.cpp @@ -35,8 +35,8 @@ #include #include -#include -#include + +#include "PStringUtils.h" //------------------------------------------------------------- // msr block header tags @@ -88,8 +88,6 @@ bool msr2msr_run(char *str, const std::size_t size) TString run(str); TString line(str); - TObjArray *tokens; - TObjString *ostr[2]; // for filtering run.ToUpper(); @@ -100,8 +98,8 @@ bool msr2msr_run(char *str, const std::size_t size) line.Remove(idx); // tokenize run - tokens = line.Tokenize(" \t"); - if (tokens->GetEntries() < 4) { + std::vector tokens = PStringUtils::Split(line.Data(), " \t"); + if (tokens.size() < 4) { std::cout << std::endl << "**ERROR**: Something is wrong with the RUN block header:"; std::cout << std::endl << " >> " << str; std::cout << std::endl << " >> no is created"; @@ -109,35 +107,22 @@ bool msr2msr_run(char *str, const std::size_t size) return false; } - if (tokens->GetEntries() == 5) { // already a new msr file, do only add the proper run comment + if (tokens.size() == 5) { // already a new msr file, do only add the proper run comment snprintf(str, size, "%s (name beamline institute data-file-format)", line.Data()); return true; } if (run.Contains("NEMU")) { - ostr[0] = dynamic_cast(tokens->At(1)); // file name - snprintf(str, size, "RUN %s MUE4 PSI WKM (name beamline institute data-file-format)", ostr[0]->GetString().Data()); + snprintf(str, size, "RUN %s MUE4 PSI WKM (name beamline institute data-file-format)", tokens[1].c_str()); } else if (run.Contains("PSI")) { - ostr[0] = dynamic_cast(tokens->At(1)); // file name - ostr[1] = dynamic_cast(tokens->At(2)); // beamline snprintf(str, size, "RUN %s %s PSI PSI-BIN (name beamline institute data-file-format)", - ostr[0]->GetString().Data(), ostr[1]->GetString().Data()); + tokens[1].c_str(), tokens[2].c_str()); } else if (run.Contains("TRIUMF")) { - ostr[0] = dynamic_cast(tokens->At(1)); // file name - ostr[1] = dynamic_cast(tokens->At(2)); // beamline snprintf(str, size, "RUN %s %s TRIUMF MUD (name beamline institute data-file-format)", - ostr[0]->GetString().Data(), ostr[1]->GetString().Data()); + tokens[1].c_str(), tokens[2].c_str()); } else if (run.Contains("RAL")) { - ostr[0] = dynamic_cast(tokens->At(1)); // file name - ostr[1] = dynamic_cast(tokens->At(2)); // beamline snprintf(str, size, "RUN %s %s RAL NEXUS (name beamline institute data-file-format)", - ostr[0]->GetString().Data(), ostr[1]->GetString().Data()); - } - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; + tokens[1].c_str(), tokens[2].c_str()); } return true; @@ -165,38 +150,34 @@ bool msr2msr_param(char *str) // handle parameter line TString line(str); - TObjArray *tokens; - TObjString *ostr[6]; char sstr[256]; char spaces[256]; - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + std::vector tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens == 4) { - for (unsigned int i=0; i<4; i++) - ostr[i] = dynamic_cast(tokens->At(i)); // number - snprintf(sstr, sizeof(sstr), "%10s", ostr[0]->GetString().Data()); + snprintf(sstr, sizeof(sstr), "%10s", tokens[0].c_str()); // name strcat(sstr, " "); - strcat(sstr, ostr[1]->GetString().Data()); + strcat(sstr, tokens[1].c_str()); memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 12-strlen(ostr[1]->GetString().Data())); + memset(spaces, ' ', 12-strlen(tokens[1].c_str())); strcat(sstr, spaces); // value - strcat(sstr, ostr[2]->GetString().Data()); - if (strlen(ostr[2]->GetString().Data()) < 10) { + strcat(sstr, tokens[2].c_str()); + if (strlen(tokens[2].c_str()) < 10) { memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 10-strlen(ostr[2]->GetString().Data())); + memset(spaces, ' ', 10-strlen(tokens[2].c_str())); strcat(sstr, spaces); } else { strcat(sstr, " "); } // step - strcat(sstr, ostr[3]->GetString().Data()); - if (strlen(ostr[3]->GetString().Data()) < 12) { + strcat(sstr, tokens[3].c_str()); + if (strlen(tokens[3].c_str()) < 12) { memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 12-strlen(ostr[3]->GetString().Data())); + memset(spaces, ' ', 12-strlen(tokens[3].c_str())); strcat(sstr, spaces); } else { strcat(sstr, " "); @@ -204,30 +185,28 @@ bool msr2msr_param(char *str) strcat(sstr, "none"); strcpy(str, sstr); } else if (noTokens == 6) { - for (unsigned int i=0; i<6; i++) - ostr[i] = dynamic_cast(tokens->At(i)); // number - snprintf(sstr, sizeof(sstr), "%10s", ostr[0]->GetString().Data()); + snprintf(sstr, sizeof(sstr), "%10s", tokens[0].c_str()); // name strcat(sstr, " "); - strcat(sstr, ostr[1]->GetString().Data()); + strcat(sstr, tokens[1].c_str()); memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 12-strlen(ostr[1]->GetString().Data())); + memset(spaces, ' ', 12-strlen(tokens[1].c_str())); strcat(sstr, spaces); // value - strcat(sstr, ostr[2]->GetString().Data()); - if (strlen(ostr[2]->GetString().Data()) < 10) { + strcat(sstr, tokens[2].c_str()); + if (strlen(tokens[2].c_str()) < 10) { memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 10-strlen(ostr[2]->GetString().Data())); + memset(spaces, ' ', 10-strlen(tokens[2].c_str())); strcat(sstr, spaces); } else { strcat(sstr, " "); } // step - strcat(sstr, ostr[3]->GetString().Data()); - if (strlen(ostr[3]->GetString().Data()) < 12) { + strcat(sstr, tokens[3].c_str()); + if (strlen(tokens[3].c_str()) < 12) { memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 12-strlen(ostr[3]->GetString().Data())); + memset(spaces, ' ', 12-strlen(tokens[3].c_str())); strcat(sstr, spaces); } else { strcat(sstr, " "); @@ -235,25 +214,19 @@ bool msr2msr_param(char *str) // pos. error strcat(sstr, "none "); // lower boundary - strcat(sstr, ostr[4]->GetString().Data()); - if (strlen(ostr[4]->GetString().Data()) < 8) { + strcat(sstr, tokens[4].c_str()); + if (strlen(tokens[4].c_str()) < 8) { memset(spaces, 0, sizeof(spaces)); - memset(spaces, ' ', 8-strlen(ostr[4]->GetString().Data())); + memset(spaces, ' ', 8-strlen(tokens[4].c_str())); strcat(sstr, spaces); } else { strcat(sstr, " "); } // upper boundary - strcat(sstr, ostr[5]->GetString().Data()); + strcat(sstr, tokens[5].c_str()); strcpy(str, sstr); } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } - return true; } @@ -274,8 +247,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) { // handle theory line TString line(str); - TObjArray *tokens; - TObjString *ostr; + std::vector tokens; char sstr[256]; if ((line.Contains("sktt") || line.Contains("statKTTab")) && line.Contains("glf")) { // static Gauss KT LF table @@ -283,8 +255,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcpy(sstr, "statGssKTLF "); // tokenize the rest and extract the first two parameters - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens < 3) { std::cout << std::endl << "**ERROR** in THEORY block"; std::cout << std::endl << " Line: '" << str << "' is not a valid statKTTab statement."; @@ -293,8 +265,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) } for (Int_t i=1; i<3; i++) { strcat(sstr, " "); - ostr = dynamic_cast(tokens->At(i)); - strcat(sstr, ostr->GetString().Data()); + strcat(sstr, tokens[i].c_str()); } strcat(sstr, " (frequency damping)"); strcpy(str, sstr); @@ -303,8 +274,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcpy(sstr, "statExpKTLF "); // tokenize the rest and extract the first two parameters - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens < 3) { std::cout << std::endl << "**ERROR** in THEORY block"; std::cout << std::endl << " Line: '" << str << "' is not a valid statKTTab statement."; @@ -313,8 +284,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) } for (Int_t i=1; i<3; i++) { strcat(sstr, " "); - ostr = dynamic_cast(tokens->At(i)); - strcat(sstr, ostr->GetString().Data()); + strcat(sstr, tokens[i].c_str()); } strcat(sstr, " (frequency damping)"); strcpy(str, sstr); @@ -323,8 +293,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcpy(sstr, "dynGssKTLF "); // tokenize the rest and extract the first three parameters - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens < 4) { std::cout << std::endl << "**ERROR** in THEORY block"; std::cout << std::endl << " Line: '" << str << "' is not a valid dynmKTTab statement."; @@ -333,8 +303,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) } for (Int_t i=1; i<4; i++) { strcat(sstr, " "); - ostr = dynamic_cast(tokens->At(i)); - strcat(sstr, ostr->GetString().Data()); + strcat(sstr, tokens[i].c_str()); } strcat(sstr, " (frequency damping hopping-rate)"); strcpy(str, sstr); @@ -343,8 +312,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcpy(sstr, "dynExpKTLF "); // tokenize the rest and extract the first three parameters - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens < 4) { std::cout << std::endl << "**ERROR** in THEORY block"; std::cout << std::endl << " Line: '" << str << "' is not a valid dynmKTTab statement."; @@ -353,8 +322,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) } for (Int_t i=1; i<4; i++) { strcat(sstr, " "); - ostr = dynamic_cast(tokens->At(i)); - strcat(sstr, ostr->GetString().Data()); + strcat(sstr, tokens[i].c_str()); } strcat(sstr, " (frequency damping hopping-rate)"); strcpy(str, sstr); @@ -366,8 +334,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcpy(sstr, "internFld "); // tokenize the rest and extract the first three parameters - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens < 4) { std::cout << std::endl << "**ERROR** in THEORY block"; std::cout << std::endl << " Line: '" << str << "' is not a valid internFld statement."; @@ -377,8 +345,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcat(sstr, " _x_"); for (Int_t i=1; i<4; i++) { strcat(sstr, " "); - ostr = dynamic_cast(tokens->At(i)); - strcat(sstr, ostr->GetString().Data()); + strcat(sstr, tokens[i].c_str()); } strcat(sstr, " (fraction phase frequency Trate Lrate)"); strcpy(str, sstr); @@ -390,8 +357,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcpy(sstr, "internBsl "); // tokenize the rest and extract the first three parameters - tokens = line.Tokenize(" \t"); - Int_t noTokens = tokens->GetEntries(); + tokens = PStringUtils::Split(line.Data(), " \t"); + std::size_t noTokens = tokens.size(); if (noTokens < 4) { std::cout << std::endl << "**ERROR** in THEORY block"; std::cout << std::endl << " Line: '" << str << "' is not a valid internBsl statement."; @@ -401,8 +368,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams) strcat(sstr, " _x_"); for (Int_t i=1; i<4; i++) { strcat(sstr, " "); - ostr = dynamic_cast(tokens->At(i)); - strcat(sstr, ostr->GetString().Data()); + strcat(sstr, tokens[i].c_str()); } strcat(sstr, " (fraction phase frequency Trate Lrate)"); strcpy(str, sstr); From 66aa847b58bc45264c242c895fb5e778878be384 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 15:38:15 +0200 Subject: [PATCH 12/24] PFitter: replace TObjArray/TObjString with PStringUtils Replace ROOT's TString::Tokenize() + TObjArray/TObjString token handling with the dependency-free PStringUtils::Split() across all command/theory parsing in PFitter (GetPhaseParams, GetParFromFun, CheckCommands and the Execute* helpers). Split() mirrors Tokenize() semantics (delimiter-set, skips empty tokens), so token counts and indices are unchanged. Each token is still copied into a TString, so the downstream Atoi/Atof/IsFloat/IsDigit/Contains/CompareTo logic stays as-is. Using a std::vector removes the manual TObjArray cleanup and incidentally fixes three pre-existing leaks: the tokens array in ExecuteFitRange was never freed, and the early return paths in ExecutePrintLevel and the SECTOR check in CheckCommands skipped the cleanup. Build of libPMusr and musrfit is clean; full ctest suite passes (85/85). Co-Authored-By: Claude Opus 4.8 --- src/classes/PFitter.cpp | 300 ++++++++-------------------------------- 1 file changed, 61 insertions(+), 239 deletions(-) diff --git a/src/classes/PFitter.cpp b/src/classes/PFitter.cpp index a25e1d69..2d339433 100644 --- a/src/classes/PFitter.cpp +++ b/src/classes/PFitter.cpp @@ -64,10 +64,9 @@ #include #include #include -#include -#include #include "PFitter.h" +#include "PStringUtils.h" //+++ PSectorChisq class +++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -397,8 +396,6 @@ void PFitter::GetPhaseParams() // default functions: // user functions cannot be checked! PMsrLines *theo = fRunInfo->GetMsrTheory(); - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; int pos = -1; for (unsigned int i=0; isize(); i++) { @@ -423,18 +420,10 @@ void PFitter::GetPhaseParams() continue; // extract phase token - tok = line.Tokenize(" \t"); - if (tok == nullptr) { - std::cerr << "PFitter::GetPhaseParams(): **ERROR** couldn't tokenize theory line string." << std::endl; - return; + std::vector tok = PStringUtils::Split(line.Data(), " \t"); + if (static_cast(tok.size()) > pos) { + str = tok[pos].c_str(); } - if (tok->GetEntries() > pos) { - ostr = dynamic_cast(tok->At(pos)); - str = ostr->GetString(); - } - // clean up - delete tok; - tok = nullptr; // decode phase token. It can be funX, mapX, or a number if (str.Contains("fun")) { // function @@ -491,22 +480,15 @@ PIntVector PFitter::GetParFromFun(const TString funStr) PIntVector parVec; PMsrLines *funList = fRunInfo->GetMsrFunctions(); - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; for (int i=0; isize(); i++) { if (funList->at(i).fLine.Contains(funStr)) { // tokenize function string - tok = funList->at(i).fLine.Tokenize(" =+-*/"); - if (tok == nullptr) { - std::cerr << "PFitter::GetParFromFun(): **ERROR** couldn't tokenize function string." << std::endl; - return parVec; - } + std::vector tok = PStringUtils::Split(funList->at(i).fLine.Data(), " =+-*/"); - for (int j=1; jGetEntries(); j++) { - ostr = dynamic_cast(tok->At(j)); - str = ostr->GetString(); + for (int j=1; j(tok.size()); j++) { + str = tok[j].c_str(); // parse tok for parX if (str.Contains("par")) { // find start idx of par in token @@ -533,10 +515,6 @@ PIntVector PFitter::GetParFromFun(const TString funStr) } } } - - // clean up - delete tok; - tok = nullptr; } } @@ -996,16 +974,14 @@ Bool_t PFitter::CheckCommands() cmd.second = cmdLineNo; fCmdList.push_back(cmd); // filter out possible parameters for scan - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; UInt_t ival; - tokens = line.Tokenize(", \t"); + tokens = PStringUtils::Split(line.Data(), ", \t"); - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (Int_t i=0; i(tokens.size()); i++) { + str = tokens[i].c_str(); if ((i==1) || (i==2)) { // parX / parY // check that token is a UInt_t @@ -1016,10 +992,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } ival = str.Atoi(); @@ -1031,10 +1003,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } // keep parameter @@ -1051,10 +1019,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } ival = str.Atoi(); @@ -1065,20 +1029,12 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } fScanNoPoints = ival; } } - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("EIGEN", TString::kIgnoreCase)) { cmd.first = PMN_EIGEN; cmd.second = cmdLineNo; @@ -1090,15 +1046,13 @@ Bool_t PFitter::CheckCommands() // (iii) FIT_RANGE start1 end1 start2 end2 ... startN endN // (iv) FIT_RANGE fgb+n0 lgb-n1 // (v) FIT_RANGE fgb+n00 lgb-n01 fgb+n10 lgb-n11 ... fgb+nN0 lgb-nN1 - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = line.Tokenize(", \t"); + tokens = PStringUtils::Split(line.Data(), ", \t"); - if (tokens->GetEntries() == 2) { // should only be RESET - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); + if (static_cast(tokens.size()) == 2) { // should only be RESET + str = tokens[1].c_str(); if (str.Contains("RESET", TString::kIgnoreCase)) { cmd.first = PMN_FIT_RANGE; cmd.second = cmdLineNo; @@ -1110,32 +1064,23 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> with N the number of runs in the msr-file." << std::endl; std::cerr << std::endl << ">> Found " << str.Data() << ", instead of RESET" << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } - } else if ((tokens->GetEntries() > 1) && (static_cast(tokens->GetEntries()) % 2) == 1) { - if ((tokens->GetEntries() > 3) && ((static_cast(tokens->GetEntries())-1)) != 2*fRunInfo->GetMsrRunList()->size()) { + } else if ((static_cast(tokens.size()) > 1) && (static_cast(static_cast(tokens.size())) % 2) == 1) { + if ((static_cast(tokens.size()) > 3) && ((static_cast(static_cast(tokens.size()))-1)) != 2*fRunInfo->GetMsrRunList()->size()) { std::cerr << std::endl << ">> PFitter::CheckCommands: **ERROR** in line " << it->fLineNo; std::cerr << std::endl << ">> " << line.Data(); std::cerr << std::endl << ">> Syntax: FIT_RANGE RESET | FIT_RANGE | FIT_RANGE .. |"; std::cerr << std::endl << ">> FIT_RANGE fgb+ lgb- | FIT_RANGE fgb+ lgb- fgb+ lgb- ... fgb+ lgb-,"; std::cerr << std::endl << ">> with N the number of runs in the msr-file."; - std::cerr << std::endl << ">> Found N=" << (tokens->GetEntries()-1)/2 << ", # runs in msr-file=" << fRunInfo->GetMsrRunList()->size() << std::endl; + std::cerr << std::endl << ">> Found N=" << (static_cast(tokens.size())-1)/2 << ", # runs in msr-file=" << fRunInfo->GetMsrRunList()->size() << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } else { // check that all range entries are numbers or fgb+n0 / lgb-n1 Bool_t ok = true; - for (Int_t n=1; nGetEntries(); n++) { - ostr = dynamic_cast(tokens->At(n)); - str = ostr->GetString(); + for (Int_t n=1; n(tokens.size()); n++) { + str = tokens[n].c_str(); if (!str.IsFloat()) { if ((n%2 == 1) && (!str.Contains("fgb", TString::kIgnoreCase))) ok = false; @@ -1158,10 +1103,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> with N the number of runs in the msr-file."; std::cerr << std::endl << ">> Found token '" << str.Data() << "', which is not a floating point number." << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } } @@ -1172,29 +1113,19 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> FIT_RANGE fgb+ lgb- | FIT_RANGE fgb+ lgb- fgb+ lgb- ... fgb+ lgb-,"; std::cerr << std::endl << ">> with N the number of runs in the msr-file."; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("FIX", TString::kIgnoreCase)) { // check if the given set of parameters (number or names) is present - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; UInt_t ival; - tokens = line.Tokenize(", \t"); + tokens = PStringUtils::Split(line.Data(), ", \t"); - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (Int_t i=1; i(tokens.size()); i++) { + str = tokens[i].c_str(); if (str.IsDigit()) { // token might be a parameter number ival = str.Atoi(); @@ -1205,10 +1136,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> Parameter " << ival << " is out of the Parameter Range [1," << fParams.size() << "]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } } else { // token might be a parameter name @@ -1226,19 +1153,11 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> Parameter '" << str.Data() << "' is NOT present as a parameter name"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } } } - if (tokens) { - delete tokens; - tokens = nullptr; - } // everything looks fine, feed the command list cmd.first = PMN_FIX; @@ -1278,16 +1197,14 @@ Bool_t PFitter::CheckCommands() fCmdList.push_back(cmd); } else if (line.Contains("RELEASE", TString::kIgnoreCase)) { // check if the given set of parameters (number or names) is present - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; UInt_t ival; - tokens = line.Tokenize(", \t"); + tokens = PStringUtils::Split(line.Data(), ", \t"); - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (Int_t i=1; i(tokens.size()); i++) { + str = tokens[i].c_str(); if (str.IsDigit()) { // token might be a parameter number ival = str.Atoi(); @@ -1298,10 +1215,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> Parameter " << ival << " is out of the Parameter Range [1," << fParams.size() << "]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } } else { // token might be a parameter name @@ -1319,19 +1232,11 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> Parameter '" << str.Data() << "' is NOT present as a parameter name"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } } } - if (tokens) { - delete tokens; - tokens = nullptr; - } cmd.first = PMN_RELEASE; cmd.second = cmdLineNo; fCmdList.push_back(cmd); @@ -1348,16 +1253,14 @@ Bool_t PFitter::CheckCommands() cmd.second = cmdLineNo; fCmdList.push_back(cmd); // filter out possible parameters for scan - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; UInt_t ival; - tokens = line.Tokenize(", \t"); + tokens = PStringUtils::Split(line.Data(), ", \t"); - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (Int_t i=0; i(tokens.size()); i++) { + str = tokens[i].c_str(); if (i==1) { // get parameter number // check that token is a UInt_t if (!str.IsDigit()) { @@ -1367,10 +1270,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } ival = str.Atoi(); @@ -1382,10 +1281,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } // keep parameter @@ -1402,10 +1297,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } ival = str.Atoi(); @@ -1416,10 +1307,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } fScanNoPoints = ival; @@ -1434,10 +1321,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } fScanLow = str.Atof(); @@ -1452,33 +1335,23 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]"; std::cerr << std::endl; fIsValid = false; - if (tokens) { - delete tokens; - tokens = nullptr; - } break; } fScanHigh = str.Atof(); } } - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("SIMPLEX", TString::kIgnoreCase)) { cmd.first = PMN_SIMPLEX; cmd.second = cmdLineNo; fCmdList.push_back(cmd); } else if (line.Contains("STRATEGY", TString::kIgnoreCase)) { - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = line.Tokenize(" \t"); - if (tokens->GetEntries() == 2) { - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); + tokens = PStringUtils::Split(line.Data(), " \t"); + if (static_cast(tokens.size()) == 2) { + str = tokens[1].c_str(); if (str.CompareTo("0") == 0) { // low fStrategy = 0; } else if (str.CompareTo("1") == 0) { // default @@ -1494,10 +1367,6 @@ Bool_t PFitter::CheckCommands() } } - if (tokens) { - delete tokens; - tokens = nullptr; - } } else if (line.Contains("USER_COVARIANCE", TString::kIgnoreCase)) { cmd.first = PMN_USER_COVARIANCE; cmd.second = cmdLineNo; @@ -1513,35 +1382,28 @@ Bool_t PFitter::CheckCommands() fCmdList.push_back(cmd); // check if the given sector arguments are valid time stamps, i.e. doubles and value < lgb time stamp - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = line.Tokenize(" ,\t"); + tokens = PStringUtils::Split(line.Data(), " ,\t"); - if (tokens->GetEntries() == 1) { // no sector time stamps given -> issue an error + if (static_cast(tokens.size()) == 1) { // no sector time stamps given -> issue an error std::cerr << std::endl << ">> PFitter::CheckCommands(): **FATAL ERROR** in line " << it->fLineNo; std::cerr << std::endl << ">> " << line.Data(); std::cerr << std::endl << ">> At least one sector time stamp is expected."; std::cerr << std::endl << ">> Will stop ..."; std::cerr << std::endl; - // cleanup - if (tokens) { - delete tokens; - tokens = nullptr; - } fIsValid = false; fSectorFlag = false; break; } Double_t dval; - for (Int_t i=1; iGetEntries(); i++) { + for (Int_t i=1; i(tokens.size()); i++) { // keep time range of sector PSectorChisq sec(fRunInfo->GetNoOfRuns()); // get parse tokens - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + str = tokens[i].c_str(); if (str.IsFloat()) { dval = str.Atof(); // check that the sector time stamp is smaller than all lgb time stamps @@ -1552,11 +1414,6 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> The sector time stamp " << dval << " is > as the lgb time stamp (" << fOriginalFitRange[j].second << ") of run " << j << "."; std::cerr << std::endl << ">> Will stop ..."; std::cerr << std::endl; - // cleanup - if (tokens) { - delete tokens; - tokens = nullptr; - } fIsValid = false; fSectorFlag = false; return fIsValid; @@ -1571,21 +1428,12 @@ Bool_t PFitter::CheckCommands() std::cerr << std::endl << ">> The sector time stamp '" << str << "' is not a number."; std::cerr << std::endl << ">> Will stop ..."; std::cerr << std::endl; - // cleanup - if (tokens) { - delete tokens; - tokens = nullptr; - } fIsValid = false; fSectorFlag = false; break; } } - if (tokens) { - delete tokens; - tokens = nullptr; - } } else { // unkown command std::cerr << std::endl << ">> PFitter::CheckCommands(): **FATAL ERROR** in line " << it->fLineNo << " an unkown command is found:"; std::cerr << std::endl << ">> " << line.Data(); @@ -1729,27 +1577,24 @@ Bool_t PFitter::ExecuteFitRange(UInt_t lineNo) return true; } - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = fCmdLines[lineNo].fLine.Tokenize(", \t"); + tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t"); PMsrRunList *runList = fRunInfo->GetMsrRunList(); // execute command, no error checking needed since this has been already carried out in CheckCommands() - if (tokens->GetEntries() == 2) { // reset command + if (static_cast(tokens.size()) == 2) { // reset command fRunListCollection->SetFitRange(fOriginalFitRange); - } else if (tokens->GetEntries() == 3) { // single fit range for all runs + } else if (static_cast(tokens.size()) == 3) { // single fit range for all runs Double_t start = 0.0, end = 0.0; PDoublePair fitRange; PDoublePairVector fitRangeVector; - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); + str = tokens[1].c_str(); start = str.Atof(); - ostr = dynamic_cast(tokens->At(2)); - str = ostr->GetString(); + str = tokens[2].c_str(); end = str.Atof(); fitRange.first = start; @@ -1763,11 +1608,9 @@ Bool_t PFitter::ExecuteFitRange(UInt_t lineNo) PDoublePairVector fitRangeVector; for (UInt_t i=0; isize(); i++) { - ostr = dynamic_cast(tokens->At(2*i+1)); - str = ostr->GetString(); + str = tokens[2*i+1].c_str(); start = str.Atof(); - ostr = dynamic_cast(tokens->At(2*i+2)); - str = ostr->GetString(); + str = tokens[2*i+2].c_str(); end = str.Atof(); fitRange.first = start; @@ -1795,15 +1638,13 @@ Bool_t PFitter::ExecuteFix(UInt_t lineNo) { std::cout << ">> PFitter::ExecuteFix(): " << fCmdLines[lineNo].fLine.Data() << std::endl; - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = fCmdLines[lineNo].fLine.Tokenize(", \t"); + tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t"); - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (Int_t i=1; i(tokens.size()); i++) { + str = tokens[i].c_str(); if (str.IsDigit()) { // token is a parameter number fMnUserParams.Fix(static_cast(str.Atoi())-1); @@ -1812,11 +1653,6 @@ Bool_t PFitter::ExecuteFix(UInt_t lineNo) } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } return true; } @@ -2124,19 +1960,17 @@ Bool_t PFitter::ExecutePrintLevel(UInt_t lineNo) { std::cout << ">> PFitter::ExecutePrintLevel(): " << fCmdLines[lineNo].fLine.Data() << std::endl; - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = fCmdLines[lineNo].fLine.Tokenize(", \t"); + tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t"); - if (tokens->GetEntries() < 2) { + if (static_cast(tokens.size()) < 2) { std::cerr << std::endl << "**ERROR** from PFitter::ExecutePrintLevel(): SYNTAX: PRINT_LEVEL , where =0-3" << std::endl << std::endl; return false; } - ostr = (TObjString*)tokens->At(1); - str = ostr->GetString(); + str = tokens[1].c_str(); Int_t ival; if (str.IsDigit()) { @@ -2159,11 +1993,6 @@ Bool_t PFitter::ExecutePrintLevel(UInt_t lineNo) ROOT::Minuit2::MnPrint::SetLevel(fPrintLevel); #endif - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } return true; } @@ -2180,17 +2009,15 @@ Bool_t PFitter::ExecutePrintLevel(UInt_t lineNo) */ Bool_t PFitter::ExecuteRelease(UInt_t lineNo) { - TObjArray *tokens = nullptr; - TObjString *ostr; + std::vector tokens; TString str; - tokens = fCmdLines[lineNo].fLine.Tokenize(", \t"); + tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t"); std::cout << ">> PFitter::ExecuteRelease(): " << fCmdLines[lineNo].fLine.Data() << std::endl; - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (Int_t i=1; i(tokens.size()); i++) { + str = tokens[i].c_str(); if (str.IsDigit()) { // token is a parameter number fMnUserParams.Release(static_cast(str.Atoi())-1); @@ -2203,11 +2030,6 @@ Bool_t PFitter::ExecuteRelease(UInt_t lineNo) } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } return true; } From 5bbcb37370a20805ec20dedd2fec799b12a450f7 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 15:45:24 +0200 Subject: [PATCH 13/24] PMusrCanvas: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PMusrCanvas.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/classes/PMusrCanvas.cpp b/src/classes/PMusrCanvas.cpp index 0f3e465d..58a6ab1d 100644 --- a/src/classes/PMusrCanvas.cpp +++ b/src/classes/PMusrCanvas.cpp @@ -34,11 +34,11 @@ #include #include #include -#include #include #include "PMusrCanvas.h" #include "PFourier.h" +#include "PStringUtils.h" static const char *gFiletypes[] = { "Data files", "*.dat", "All files", "*", @@ -6408,23 +6408,15 @@ Bool_t PMusrCanvas::IsScaleN0AndBkg() PMsrLines *cmd = fMsrHandler->GetMsrCommands(); for (UInt_t i=0; isize(); i++) { if (cmd->at(i).fLine.Contains("SCALE_N0_BKG", TString::kIgnoreCase)) { - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; - TString str; - tokens = cmd->at(i).fLine.Tokenize(" \t"); - if (tokens->GetEntries() != 2) { + std::vector tokens = PStringUtils::Split(cmd->at(i).fLine.Data(), " \t"); + if (tokens.size() != 2) { std::cerr << std::endl << ">> PRunSingleHisto::IsScaleN0AndBkg(): **WARNING** Found uncorrect 'SCALE_N0_BKG' command, will ignore it."; std::cerr << std::endl << ">> Allowed commands: SCALE_N0_BKG TRUE | FALSE" << std::endl; return willScale; } - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("FALSE", TString::kIgnoreCase)) { + if (PStringUtils::IsEqualNoCase(tokens[1], "FALSE")) { willScale = false; } - // clean up - if (tokens) - delete tokens; } } From 82f668c140d97025aa356ab232c86c98c766b630 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:17:05 +0200 Subject: [PATCH 14/24] PTheory: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PTheory.cpp | 96 +++++++++++------------------------------ 1 file changed, 24 insertions(+), 72 deletions(-) diff --git a/src/classes/PTheory.cpp b/src/classes/PTheory.cpp index 9ad72837..1bed428c 100644 --- a/src/classes/PTheory.cpp +++ b/src/classes/PTheory.cpp @@ -29,19 +29,19 @@ #include #include +#include #include #include #include #include -#include -#include #include #include #include #include "PMsrHandler.h" +#include "PStringUtils.h" #include "PTheory.h" #define SQRT_TWO 1.41421356237 @@ -169,18 +169,14 @@ PTheory::PTheory(PMsrHandler *msrInfo, UInt_t runNo, const Bool_t hasParent) : f str.Resize(index); // tokenize line - TObjArray *tokens; - TObjString *ostr; - - tokens = str.Tokenize(" \t"); - if (!tokens) { + std::vector tokens = PStringUtils::Split(str.Data(), " \t"); + if (tokens.empty()) { std::cerr << std::endl << ">> PTheory::PTheory: **SEVERE ERROR** Couldn't tokenize theory block line " << line->fLineNo << "."; std::cerr << std::endl << ">> line content: " << line->fLine.Data(); std::cerr << std::endl; exit(0); } - ostr = dynamic_cast(tokens->At(0)); - str = ostr->GetString(); + str = tokens[0]; // search the theory function UInt_t idx = SearchDataBase(str); @@ -195,11 +191,11 @@ PTheory::PTheory(PMsrHandler *msrInfo, UInt_t runNo, const Bool_t hasParent) : f } // line is a valid function, hence analyze parameters - if ((static_cast(tokens->GetEntries()-1) < fNoOfParam) && + if ((static_cast(tokens.size()-1) < fNoOfParam) && ((idx != THEORY_USER_FCN) && (idx != THEORY_POLYNOM))) { std::cerr << std::endl << ">> PTheory::PTheory: **ERROR** Theory line '" << line->fLine.Data() << "'"; std::cerr << std::endl << ">> in line no " << line->fLineNo; - std::cerr << std::endl << ">> expecting " << fgTheoDataBase[idx].fNoOfParam << ", but found " << tokens->GetEntries()-1; + std::cerr << std::endl << ">> expecting " << fgTheoDataBase[idx].fNoOfParam << ", but found " << tokens.size()-1; std::cerr << std::endl; fValid = false; } @@ -209,9 +205,8 @@ PTheory::PTheory(PMsrHandler *msrInfo, UInt_t runNo, const Bool_t hasParent) : f Int_t status; UInt_t value; Bool_t ok = false; - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (UInt_t i=1; i> See line no " << line->fLineNo; std::cerr << std::endl; fValid = false; - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } return; } else if (!TClass::GetDict(fUserFcnClassName.Data())) { std::cerr << std::endl << ">> PTheory::PTheory: **ERROR** user function class '" << fUserFcnClassName.Data() << "' not found."; @@ -316,11 +306,6 @@ PTheory::PTheory(PMsrHandler *msrInfo, UInt_t runNo, const Bool_t hasParent) : f std::cerr << std::endl << ">> See line no " << line->fLineNo; std::cerr << std::endl; fValid = false; - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } return; } } @@ -347,12 +332,6 @@ PTheory::PTheory(PMsrHandler *msrInfo, UInt_t runNo, const Bool_t hasParent) : f } } } - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } //-------------------------------------------------------------------------- @@ -945,8 +924,6 @@ void PTheory::MakeCleanAndTidyTheoryBlock(PMsrLines *fullTheoryBlock) PMsrLineStructure *line; TString str, tidy; Char_t substr[256]; - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; Int_t idx = THEORY_UNDEFINED; for (UInt_t i=1; isize(); i++) { @@ -959,10 +936,11 @@ void PTheory::MakeCleanAndTidyTheoryBlock(PMsrLines *fullTheoryBlock) if (index > 0) // theory line comment present str.Resize(index); // tokenize line - tokens = str.Tokenize(" \t"); + std::vector tokens = PStringUtils::Split(str.Data(), " \t"); + if (tokens.empty()) + continue; // make a handable string out of the asymmetry token - ostr = dynamic_cast(tokens->At(0)); - str = ostr->GetString(); + str = tokens[0]; // check if the line is just a '+' if so nothing to be done if (str.Contains("+")) continue; @@ -987,14 +965,13 @@ void PTheory::MakeCleanAndTidyTheoryBlock(PMsrLines *fullTheoryBlock) if (idx == THEORY_UNDEFINED) return; // check that there enough tokens. This should not be necessay at this point but ... - if (static_cast(tokens->GetEntries()) < fgTheoDataBase[idx].fNoOfParam + 1) + if (static_cast(tokens.size()) < fgTheoDataBase[idx].fNoOfParam + 1) return; // make tidy string snprintf(substr, sizeof(substr), "%-10s", fgTheoDataBase[idx].fName.Data()); tidy = TString(substr); - for (Int_t j=1; jGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); + for (UInt_t j=1; j(tokens->GetEntries()) == fgTheoDataBase[idx].fNoOfParam + 1) // no tshift + if (static_cast(tokens.size()) == fgTheoDataBase[idx].fNoOfParam + 1) // no tshift tidy += fgTheoDataBase[idx].fComment; else tidy += fgTheoDataBase[idx].fCommentTimeShift; } // write tidy string back into theory block (*fullTheoryBlock)[i].fLine = tidy; - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } } @@ -1041,8 +1012,6 @@ void PTheory::MakeCleanAndTidyPolynom(UInt_t i, PMsrLines *fullTheoryBlock) { PMsrLineStructure *line; TString str, tidy; - TObjArray *tokens = nullptr; - TObjString *ostr; Char_t substr[256]; // init tidy @@ -1052,13 +1021,12 @@ void PTheory::MakeCleanAndTidyPolynom(UInt_t i, PMsrLines *fullTheoryBlock) // copy line content to str in order to remove comments str = line->fLine.Copy(); // tokenize line - tokens = str.Tokenize(" \t"); + std::vector tokens = PStringUtils::Split(str.Data(), " \t"); // check if comment is already present, and if yes ignore it by setting max correctly - Int_t max = tokens->GetEntries(); + Int_t max = static_cast(tokens.size()); for (Int_t j=1; j(tokens->At(j)); - str = ostr->GetString(); + str = tokens[j]; if (str.Contains("(")) { // comment present max=j; break; @@ -1066,8 +1034,7 @@ void PTheory::MakeCleanAndTidyPolynom(UInt_t i, PMsrLines *fullTheoryBlock) } for (Int_t j=1; j(tokens->At(j)); - str = ostr->GetString(); + str = tokens[j]; snprintf(substr, sizeof(substr), "%6s", str.Data()); tidy += TString(substr); } @@ -1077,12 +1044,6 @@ void PTheory::MakeCleanAndTidyPolynom(UInt_t i, PMsrLines *fullTheoryBlock) // write tidy string back into theory block (*fullTheoryBlock)[i].fLine = tidy; - - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } //-------------------------------------------------------------------------- @@ -1104,8 +1065,6 @@ void PTheory::MakeCleanAndTidyUserFcn(UInt_t i, PMsrLines *fullTheoryBlock) { PMsrLineStructure *line; TString str, tidy; - TObjArray *tokens = nullptr; - TObjString *ostr; // init tidy tidy = TString("userFcn "); @@ -1114,22 +1073,15 @@ void PTheory::MakeCleanAndTidyUserFcn(UInt_t i, PMsrLines *fullTheoryBlock) // copy line content to str in order to remove comments str = line->fLine.Copy(); // tokenize line - tokens = str.Tokenize(" \t"); + std::vector tokens = PStringUtils::Split(str.Data(), " \t"); - for (Int_t j=1; jGetEntries(); j++) { - ostr = dynamic_cast(tokens->At(j)); - str = ostr->GetString(); + for (UInt_t j=1; j Date: Sat, 6 Jun 2026 16:20:42 +0200 Subject: [PATCH 15/24] PRunBase: drop unused TObjArray/TObjString includes Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunBase.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/classes/PRunBase.cpp b/src/classes/PRunBase.cpp index a3f2d467..4bb89453 100644 --- a/src/classes/PRunBase.cpp +++ b/src/classes/PRunBase.cpp @@ -32,8 +32,6 @@ #include #include #include -#include -#include #include #include "PRunBase.h" From f3abe77e14e578373ec0fbbab96f5ebc51526eb6 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:23:11 +0200 Subject: [PATCH 16/24] PRunAsymmetryBNMR: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunAsymmetryBNMR.cpp | 34 +++++++++++-------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/classes/PRunAsymmetryBNMR.cpp b/src/classes/PRunAsymmetryBNMR.cpp index 78f8d90e..353ac9e3 100644 --- a/src/classes/PRunAsymmetryBNMR.cpp +++ b/src/classes/PRunAsymmetryBNMR.cpp @@ -39,12 +39,13 @@ #include #include +#include +#include #include -#include -#include #include "PMusr.h" +#include "PStringUtils.h" #include "PRunAsymmetryBNMR.h" //-------------------------------------------------------------------------- @@ -406,18 +407,15 @@ UInt_t PRunAsymmetryBNMR::GetNoOfFitBins() */ void PRunAsymmetryBNMR::SetFitRangeBin(const TString fitRange) { - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; Ssiz_t idx = -1; Int_t offset = 0; - tok = fitRange.Tokenize(" \t"); + std::vector tok = PStringUtils::Split(fitRange.Data(), " \t"); - if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 + if (tok.size() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(1)); - str = ostr->GetString(); + str = tok[1]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -428,8 +426,7 @@ void PRunAsymmetryBNMR::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(2)); - str = ostr->GetString(); + str = tok[2]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -438,16 +435,15 @@ void PRunAsymmetryBNMR::SetFitRangeBin(const TString fitRange) offset = str.Atoi(); } fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution; - } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] - Int_t pos = 2*(fRunNo+1)-1; + } else if ((tok.size() > 3) && (tok.size() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] + UInt_t pos = 2*(fRunNo+1)-1; - if (pos + 1 >= tok->GetEntries()) { + if (pos + 1 >= tok.size()) { std::cerr << std::endl << ">> PRunAsymmetryBNMR::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } else { // handle fgb+n0 entry - ostr = static_cast(tok->At(pos)); - str = ostr->GetString(); + str = tok[pos]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -458,8 +454,7 @@ void PRunAsymmetryBNMR::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = static_cast(tok->At(pos+1)); - str = ostr->GetString(); + str = tok[pos+1]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -473,11 +468,6 @@ void PRunAsymmetryBNMR::SetFitRangeBin(const TString fitRange) std::cerr << std::endl << ">> PRunAsymmetryBNMR::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } - - // clean up - if (tok) { - delete tok; - } } //-------------------------------------------------------------------------- From 850fb6bbc6810c272df2c012be80a83e39c82d6b Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:26:00 +0200 Subject: [PATCH 17/24] PRunAsymmetry: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunAsymmetry.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/classes/PRunAsymmetry.cpp b/src/classes/PRunAsymmetry.cpp index cdafa3d6..6c3b2038 100644 --- a/src/classes/PRunAsymmetry.cpp +++ b/src/classes/PRunAsymmetry.cpp @@ -38,12 +38,13 @@ #include #include +#include +#include #include -#include -#include #include "PMusr.h" +#include "PStringUtils.h" #include "PRunAsymmetry.h" //-------------------------------------------------------------------------- @@ -404,18 +405,15 @@ UInt_t PRunAsymmetry::GetNoOfFitBins() */ void PRunAsymmetry::SetFitRangeBin(const TString fitRange) { - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; Ssiz_t idx = -1; Int_t offset = 0; - tok = fitRange.Tokenize(" \t"); + std::vector tok = PStringUtils::Split(fitRange.Data(), " \t"); - if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 + if (tok.size() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(1)); - str = ostr->GetString(); + str = tok[1]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -426,8 +424,7 @@ void PRunAsymmetry::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(2)); - str = ostr->GetString(); + str = tok[2]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -436,16 +433,15 @@ void PRunAsymmetry::SetFitRangeBin(const TString fitRange) offset = str.Atoi(); } fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution; - } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] - Int_t pos = 2*(fRunNo+1)-1; + } else if ((tok.size() > 3) && (tok.size() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] + UInt_t pos = 2*(fRunNo+1)-1; - if (pos + 1 >= tok->GetEntries()) { + if (pos + 1 >= tok.size()) { std::cerr << std::endl << ">> PRunAsymmetry::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } else { // handle fgb+n0 entry - ostr = static_cast(tok->At(pos)); - str = ostr->GetString(); + str = tok[pos]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -456,8 +452,7 @@ void PRunAsymmetry::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = static_cast(tok->At(pos+1)); - str = ostr->GetString(); + str = tok[pos+1]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -471,11 +466,6 @@ void PRunAsymmetry::SetFitRangeBin(const TString fitRange) std::cerr << std::endl << ">> PRunAsymmetry::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } - - // clean up - if (tok) { - delete tok; - } } //-------------------------------------------------------------------------- From 5939db272293e84fb75bab3438e266d2187e6fd1 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:26:00 +0200 Subject: [PATCH 18/24] PRunAsymmetryRRF: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunAsymmetryRRF.cpp | 34 +++++++++++--------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/classes/PRunAsymmetryRRF.cpp b/src/classes/PRunAsymmetryRRF.cpp index a73720cc..c961f951 100644 --- a/src/classes/PRunAsymmetryRRF.cpp +++ b/src/classes/PRunAsymmetryRRF.cpp @@ -39,12 +39,13 @@ #include #include +#include +#include #include -#include -#include #include "PMusr.h" +#include "PStringUtils.h" #include "PRunAsymmetryRRF.h" //-------------------------------------------------------------------------- @@ -393,18 +394,15 @@ UInt_t PRunAsymmetryRRF::GetNoOfFitBins() */ void PRunAsymmetryRRF::SetFitRangeBin(const TString fitRange) { - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; Ssiz_t idx = -1; Int_t offset = 0; - tok = fitRange.Tokenize(" \t"); + std::vector tok = PStringUtils::Split(fitRange.Data(), " \t"); - if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 + if (tok.size() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 // handle fgb+n0 entry - ostr = static_cast(tok->At(1)); - str = ostr->GetString(); + str = tok[1]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -415,8 +413,7 @@ void PRunAsymmetryRRF::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = static_cast(tok->At(2)); - str = ostr->GetString(); + str = tok[2]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -425,16 +422,15 @@ void PRunAsymmetryRRF::SetFitRangeBin(const TString fitRange) offset = str.Atoi(); } fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution; - } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] - Int_t pos = 2*(fRunNo+1)-1; + } else if ((tok.size() > 3) && (tok.size() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] + UInt_t pos = 2*(fRunNo+1)-1; - if (pos + 1 >= tok->GetEntries()) { + if (pos + 1 >= tok.size()) { std::cerr << std::endl << ">> PRunSingleHisto::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } else { // handle fgb+n0 entry - ostr = static_cast(tok->At(pos)); - str = ostr->GetString(); + str = tok[pos]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -445,8 +441,7 @@ void PRunAsymmetryRRF::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = static_cast(tok->At(pos+1)); - str = ostr->GetString(); + str = tok[pos+1]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -460,11 +455,6 @@ void PRunAsymmetryRRF::SetFitRangeBin(const TString fitRange) std::cerr << std::endl << ">> PRunSingleHisto::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } - - // clean up - if (tok) { - delete tok; - } } //-------------------------------------------------------------------------- From 6a93932f9083e1f141a91ebd07b59cbf4a6c9955 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:32:08 +0200 Subject: [PATCH 19/24] PRunMuMinus: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunMuMinus.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/classes/PRunMuMinus.cpp b/src/classes/PRunMuMinus.cpp index 5440e154..e1fb4707 100644 --- a/src/classes/PRunMuMinus.cpp +++ b/src/classes/PRunMuMinus.cpp @@ -36,11 +36,12 @@ #endif #include +#include +#include #include -#include -#include +#include "PStringUtils.h" #include "PRunMuMinus.h" //-------------------------------------------------------------------------- @@ -448,18 +449,15 @@ UInt_t PRunMuMinus::GetNoOfFitBins() */ void PRunMuMinus::SetFitRangeBin(const TString fitRange) { - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; Ssiz_t idx = -1; Int_t offset = 0; - tok = fitRange.Tokenize(" \t"); + std::vector tok = PStringUtils::Split(fitRange.Data(), " \t"); - if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 + if (tok.size() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(1)); - str = ostr->GetString(); + str = tok[1]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -470,8 +468,7 @@ void PRunMuMinus::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(2)); - str = ostr->GetString(); + str = tok[2]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -480,16 +477,15 @@ void PRunMuMinus::SetFitRangeBin(const TString fitRange) offset = str.Atoi(); } fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution; - } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] - Int_t pos = 2*(fRunNo+1)-1; + } else if ((tok.size() > 3) && (tok.size() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] + UInt_t pos = 2*(fRunNo+1)-1; - if (pos + 1 >= tok->GetEntries()) { + if (pos + 1 >= tok.size()) { std::cerr << std::endl << ">> PRunMuMinus::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } else { // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(pos)); - str = ostr->GetString(); + str = tok[pos]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -500,8 +496,7 @@ void PRunMuMinus::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(pos+1)); - str = ostr->GetString(); + str = tok[pos+1]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -515,11 +510,6 @@ void PRunMuMinus::SetFitRangeBin(const TString fitRange) std::cerr << std::endl << ">> PRunMuMinus::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } - - // clean up - if (tok) { - delete tok; - } } //-------------------------------------------------------------------------- From 5dbd3c74f1eca9e7a2ecfa2806930d3fdf26ffdc Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:32:08 +0200 Subject: [PATCH 20/24] PRunSingleHisto: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunSingleHisto.cpp | 48 +++++++++++---------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/src/classes/PRunSingleHisto.cpp b/src/classes/PRunSingleHisto.cpp index 1a44839c..83deca84 100644 --- a/src/classes/PRunSingleHisto.cpp +++ b/src/classes/PRunSingleHisto.cpp @@ -38,12 +38,13 @@ #include #include #include +#include +#include #include -#include -#include #include "PMusr.h" +#include "PStringUtils.h" #include "PRunSingleHisto.h" //-------------------------------------------------------------------------- @@ -844,18 +845,15 @@ UInt_t PRunSingleHisto::GetNoOfFitBins() */ void PRunSingleHisto::SetFitRangeBin(const TString fitRange) { - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; Ssiz_t idx = -1; Int_t offset = 0; - tok = fitRange.Tokenize(" \t"); + std::vector tok = PStringUtils::Split(fitRange.Data(), " \t"); - if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 + if (tok.size() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(1)); - str = ostr->GetString(); + str = tok[1]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -866,8 +864,7 @@ void PRunSingleHisto::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(2)); - str = ostr->GetString(); + str = tok[2]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -876,16 +873,15 @@ void PRunSingleHisto::SetFitRangeBin(const TString fitRange) offset = str.Atoi(); } fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution; - } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] - Int_t pos = 2*(fRunNo+1)-1; + } else if ((tok.size() > 3) && (tok.size() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] + UInt_t pos = 2*(fRunNo+1)-1; - if (pos + 1 >= tok->GetEntries()) { + if (pos + 1 >= tok.size()) { std::cerr << std::endl << ">> PRunSingleHisto::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } else { // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(pos)); - str = ostr->GetString(); + str = tok[pos]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -896,8 +892,7 @@ void PRunSingleHisto::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(pos+1)); - str = ostr->GetString(); + str = tok[pos+1]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -911,11 +906,6 @@ void PRunSingleHisto::SetFitRangeBin(const TString fitRange) std::cerr << std::endl << ">> PRunSingleHisto::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } - - // clean up - if (tok) { - delete tok; - } } //-------------------------------------------------------------------------- @@ -2430,23 +2420,15 @@ Bool_t PRunSingleHisto::IsScaleN0AndBkg() PMsrLines *cmd = fMsrInfo->GetMsrCommands(); for (UInt_t i=0; isize(); i++) { if (cmd->at(i).fLine.Contains("SCALE_N0_BKG", TString::kIgnoreCase)) { - TObjArray *tokens = nullptr; - TObjString *ostr = nullptr; - TString str; - tokens = cmd->at(i).fLine.Tokenize(" \t"); - if (tokens->GetEntries() != 2) { + std::vector tokens = PStringUtils::Split(cmd->at(i).fLine.Data(), " \t"); + if (tokens.size() != 2) { std::cerr << std::endl << ">> PRunSingleHisto::IsScaleN0AndBkg(): **WARNING** Found uncorrect 'SCALE_N0_BKG' command, will ignore it."; std::cerr << std::endl << ">> Allowed commands: SCALE_N0_BKG TRUE | FALSE" << std::endl; return willScale; } - ostr = dynamic_cast(tokens->At(1)); - str = ostr->GetString(); - if (!str.CompareTo("FALSE", TString::kIgnoreCase)) { + if (PStringUtils::IsEqualNoCase(tokens[1], "FALSE")) { willScale = false; } - // clean up - if (tokens) - delete tokens; } } From 6cb69a78a7b626bfec2f496d81b64f52d5b8e095 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:32:08 +0200 Subject: [PATCH 21/24] PRunSingleHistoRRF: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunSingleHistoRRF.cpp | 34 +++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/classes/PRunSingleHistoRRF.cpp b/src/classes/PRunSingleHistoRRF.cpp index c5119c2d..8d160656 100644 --- a/src/classes/PRunSingleHistoRRF.cpp +++ b/src/classes/PRunSingleHistoRRF.cpp @@ -39,13 +39,14 @@ #include #include #include +#include +#include #include -#include -#include #include #include "PMusr.h" +#include "PStringUtils.h" #include "PFourier.h" #include "PRunSingleHistoRRF.h" @@ -528,18 +529,15 @@ UInt_t PRunSingleHistoRRF::GetNoOfFitBins() */ void PRunSingleHistoRRF::SetFitRangeBin(const TString fitRange) { - TObjArray *tok = nullptr; - TObjString *ostr = nullptr; TString str; Ssiz_t idx = -1; Int_t offset = 0; - tok = fitRange.Tokenize(" \t"); + std::vector tok = PStringUtils::Split(fitRange.Data(), " \t"); - if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 + if (tok.size() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1 // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(1)); - str = ostr->GetString(); + str = tok[1]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -550,8 +548,7 @@ void PRunSingleHistoRRF::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(2)); - str = ostr->GetString(); + str = tok[2]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -560,16 +557,15 @@ void PRunSingleHistoRRF::SetFitRangeBin(const TString fitRange) offset = str.Atoi(); } fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution; - } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] - Int_t pos = 2*(fRunNo+1)-1; + } else if ((tok.size() > 3) && (tok.size() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]] + UInt_t pos = 2*(fRunNo+1)-1; - if (pos + 1 >= tok->GetEntries()) { + if (pos + 1 >= tok.size()) { std::cerr << std::endl << ">> PRunSingleHistoRRF::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } else { // handle fgb+n0 entry - ostr = dynamic_cast(tok->At(pos)); - str = ostr->GetString(); + str = tok[pos]; // check if there is an offset present idx = str.First("+"); if (idx != -1) { // offset present @@ -580,8 +576,7 @@ void PRunSingleHistoRRF::SetFitRangeBin(const TString fitRange) fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution; // handle lgb-n1 entry - ostr = dynamic_cast(tok->At(pos+1)); - str = ostr->GetString(); + str = tok[pos+1]; // check if there is an offset present idx = str.First("-"); if (idx != -1) { // offset present @@ -595,11 +590,6 @@ void PRunSingleHistoRRF::SetFitRangeBin(const TString fitRange) std::cerr << std::endl << ">> PRunSingleHistoRRF::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'"; std::cerr << std::endl << ">> will ignore it. Sorry ..." << std::endl; } - - // clean up - if (tok) { - delete tok; - } } //-------------------------------------------------------------------------- From 41301bd988ba75406bf1318d4e9f6b331b988248 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 16:54:46 +0200 Subject: [PATCH 22/24] PRunDataHandler: replace tokenizer TObjArray/TObjString with PStringUtils Convert all TString::Tokenize() call sites to PStringUtils::Split. The persisted RunSummary TObjArray (read from the MusrRoot file via FindObjectAny and iterated with TObjArrayIter) and the run-title TObjString returned by TLemRunHeader::GetRunTitle() remain ROOT types, since they come from external interfaces / on-disk format, not from tokenization. Co-Authored-By: Claude Opus 4.8 --- src/classes/PRunDataHandler.cpp | 543 ++++++++------------------------ 1 file changed, 126 insertions(+), 417 deletions(-) diff --git a/src/classes/PRunDataHandler.cpp b/src/classes/PRunDataHandler.cpp index 5131dd1c..9011b10c 100644 --- a/src/classes/PRunDataHandler.cpp +++ b/src/classes/PRunDataHandler.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,7 @@ #include "TLemRunHeader.h" #include "MuSR_td_PSI_bin.h" #include "mud.h" +#include "PStringUtils.h" #ifdef PNEXUS_ENABLED #include "PNeXus.h" @@ -1168,30 +1170,22 @@ Bool_t PRunDataHandler::FileExistsCheck(PMsrRunBlock &runInfo, const UInt_t idx) if (pathName.CompareTo("???") == 0) { // not found in local directory and xml path str = TString(musrpath); // MUSRFULLDATAPATH has the structure: path_1:path_2:...:path_n - TObjArray *tokens = str.Tokenize(":"); - TObjString *ostr; - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString() + TString("/") + *runInfo.GetRunName(idx); + std::vector tokens = PStringUtils::Split(str.Data(), ":"); + for (UInt_t i=0; i tokens = PStringUtils::Split(str.Data(), ":"); pstr = runInfo.GetInstitute(idx); if (pstr == nullptr) { std::cerr << std::endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain institute data." << std::endl; @@ -1206,9 +1200,8 @@ Bool_t PRunDataHandler::FileExistsCheck(PMsrRunBlock &runInfo, const UInt_t idx) } pstr->ToUpper(); runInfo.SetBeamline(*pstr, idx); - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString() + TString("/DATA/") + + for (UInt_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString() + TString("/") + fln; + std::vector tokens = PStringUtils::Split(str.Data(), ":"); + for (UInt_t i=0; iAccessPathName(str.Data())!=true) { // found pathName = str; break; } } - // cleanup - if (tokens) { - delete tokens; - tokens = nullptr; - } } // no proper path name found if (pathName.CompareTo("???") == 0) { @@ -1374,21 +1355,14 @@ Bool_t PRunDataHandler::FileExistsCheck(const TString fileName) if (pathName.CompareTo("???") == 0) { // not found in local directory and xml path str = TString(musrpath); // MUSRFULLDATAPATH has the structure: path_1:path_2:...:path_n - TObjArray *tokens = str.Tokenize(":"); - TObjString *ostr; - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString() + TString("/") + fileName; + std::vector tokens = PStringUtils::Split(str.Data(), ":"); + for (UInt_t i=0; iAccessPathName(str.Data())!=true) { // found pathName = str; break; } } - // cleanup - if (tokens) { - delete tokens; - tokens = nullptr; - } } // no proper path name found if (pathName.CompareTo("???") == 0) { @@ -1544,97 +1518,22 @@ Bool_t PRunDataHandler::ReadRootFile() TString s; TObjArrayIter summIter(runSummary); TObjString *os(dynamic_cast(summIter.Next())); - TObjArray *oa(nullptr); - TObjString *objTok(nullptr); while (os != nullptr) { s = os->GetString(); - // will put four parallel if's since it may be that more than one RA-values are on one line - if (s.Contains("RA-L")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr) { - if (!objTok->GetString().CompareTo("RA-L")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(0, objTok->GetString().Atof()); // fill RA-R value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } - } - - if (s.Contains("RA-R")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr){ - if (!objTok->GetString().CompareTo("RA-R")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if (objTok != nullptr && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(1, objTok->GetString().Atof()); // fill RA-R value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } - } - - if (s.Contains("RA-T")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr){ - if (!objTok->GetString().CompareTo("RA-T")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(2, objTok->GetString().Atof()); // fill RA-T value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } - } - - if (s.Contains("RA-B")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr){ - if (!objTok->GetString().CompareTo("RA-B")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(3, objTok->GetString().Atof()); // fill RA-B value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } + // a summary line has the structure 'RA-L = val RA-R = val ...', i.e. the value + // follows two tokens after the tag. More than one RA-value may be on one line. + std::vector oa = PStringUtils::Split(s.Data(), " "); + for (UInt_t k=0; k+2 < oa.size(); k++) { + if (oa[k+1] != "=") + continue; + if (oa[k] == "RA-L") + runData.SetRingAnode(0, TString(oa[k+2]).Atof()); + else if (oa[k] == "RA-R") + runData.SetRingAnode(1, TString(oa[k+2]).Atof()); + else if (oa[k] == "RA-T") + runData.SetRingAnode(2, TString(oa[k+2]).Atof()); + else if (oa[k] == "RA-B") + runData.SetRingAnode(3, TString(oa[k+2]).Atof()); } os = dynamic_cast(summIter.Next()); // next summary line... @@ -1962,97 +1861,22 @@ Bool_t PRunDataHandler::ReadRootFile() TString s; TObjArrayIter summIter(runSummary); TObjString *os(dynamic_cast(summIter.Next())); - TObjArray *oa(nullptr); - TObjString *objTok(nullptr); while (os != nullptr) { s = os->GetString(); - // will put four parallel if's since it may be that more than one RA-values are on one line - if (s.Contains("RA-L")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr) { - if (!objTok->GetString().CompareTo("RA-L")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(0, objTok->GetString().Atof()); // fill RA-R value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } - } - - if (s.Contains("RA-R")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr){ - if (!objTok->GetString().CompareTo("RA-R")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(1, objTok->GetString().Atof()); // fill RA-R value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } - } - - if (s.Contains("RA-T")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr){ - if (!objTok->GetString().CompareTo("RA-T")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(2, objTok->GetString().Atof()); // fill RA-T value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } - } - - if (s.Contains("RA-B")) { - oa = s.Tokenize(" "); - TObjArrayIter lineIter(oa); - objTok = dynamic_cast(lineIter.Next()); - while (objTok != nullptr){ - if (!objTok->GetString().CompareTo("RA-B")) { - objTok = dynamic_cast(lineIter.Next()); // "=" - if ((objTok != nullptr) && !objTok->GetString().CompareTo("=")) { - objTok = dynamic_cast(lineIter.Next()); // HV value - runData.SetRingAnode(3, objTok->GetString().Atof()); // fill RA-B value into the runData structure - break; - } - } - objTok = dynamic_cast(lineIter.Next()); // next token... - } - // clean up - if (oa) { - delete oa; - oa = nullptr; - } + // a summary line has the structure 'RA-L = val RA-R = val ...', i.e. the value + // follows two tokens after the tag. More than one RA-value may be on one line. + std::vector oa = PStringUtils::Split(s.Data(), " "); + for (UInt_t k=0; k+2 < oa.size(); k++) { + if (oa[k+1] != "=") + continue; + if (oa[k] == "RA-L") + runData.SetRingAnode(0, TString(oa[k+2]).Atof()); + else if (oa[k] == "RA-R") + runData.SetRingAnode(1, TString(oa[k+2]).Atof()); + else if (oa[k] == "RA-T") + runData.SetRingAnode(2, TString(oa[k+2]).Atof()); + else if (oa[k] == "RA-B") + runData.SetRingAnode(3, TString(oa[k+2]).Atof()); } os = dynamic_cast(summIter.Next()); // next summary line... @@ -2454,8 +2278,7 @@ Bool_t PRunDataHandler::ReadWkmFile() // read data --------------------------------------------------------- UInt_t group_counter = 0; Int_t val; - TObjArray *tokens; - TObjString *ostr; + std::vector tokens; TString str; UInt_t histoNo = 0; PRawRunDataSet dataSet; @@ -2489,30 +2312,22 @@ Bool_t PRunDataHandler::ReadWkmFile() f.getline(instr, sizeof(instr)); continue; } - tokens = line.Tokenize(" "); + tokens = PStringUtils::Split(line.Data(), " "); - if (!tokens) { // no tokens found + if (tokens.empty()) { // no tokens found std::cerr << std::endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: coulnd't tokenize run data."; return false; } - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (UInt_t i=0; i> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: data line contains non-integer values."; - // clean up - delete tokens; return false; } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } f.getline(instr, sizeof(instr)); @@ -2523,29 +2338,21 @@ Bool_t PRunDataHandler::ReadWkmFile() if (strlen(instr) != 0) { // extract values line = TString(instr); - tokens = line.Tokenize(" "); - if (!tokens) { // no tokens found + tokens = PStringUtils::Split(line.Data(), " "); + if (tokens.empty()) { // no tokens found std::cerr << std::endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: coulnd't tokenize run data."; return false; } - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - str = ostr->GetString(); + for (UInt_t i=0; i> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: data line contains non-integer values."; - // clean up - delete tokens; return false; } } - // clean up - if (tokens) { - delete tokens; - tokens = nullptr; - } } // save the last histo if not empty @@ -3242,8 +3049,7 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() Bool_t headerTag = false; Bool_t dataTag = false; Int_t dataLineCounter = 0; - TObjString *ostr; - TObjArray *tokens = nullptr; + std::vector tokens; TString str; Int_t groups = 0; Int_t channels = 0; @@ -3287,9 +3093,9 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() if (workStr.BeginsWith("title:", TString::kIgnoreCase)) { runData.SetRunTitle(TString(workStr.Data()+workStr.First(":")+2)); } else if (workStr.BeginsWith("field:", TString::kIgnoreCase)) { - tokens = workStr.Tokenize(":("); // field: val (units) + tokens = PStringUtils::Split(workStr.Data(), ":("); // field: val (units) // check if expected number of tokens present - if (tokens->GetEntries() != 3) { + if (tokens.size() != 3) { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", invalid field entry in header."; std::cerr << std::endl << ">> " << line.Data(); std::cerr << std::endl; @@ -3297,9 +3103,8 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() break; } // check if field value is a number - ostr = dynamic_cast(tokens->At(1)); - if (ostr->GetString().IsFloat()) { - dval = ostr->GetString().Atof(); + if (TString(tokens[1]).IsFloat()) { + dval = TString(tokens[1]).Atof(); } else { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", field value is not float/doulbe."; std::cerr << std::endl << ">> " << line.Data(); @@ -3308,10 +3113,9 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() break; } // check units, accept (G), (T) - ostr = dynamic_cast(tokens->At(2)); - if (ostr->GetString().Contains("G")) + if (TString(tokens[2]).Contains("G")) unitScaling = 1.0; - else if (ostr->GetString().Contains("T")) + else if (TString(tokens[2]).Contains("T")) unitScaling = 1.0e4; else { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", unkown field units."; @@ -3320,17 +3124,11 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() success = false; break; } - runData.SetField(dval*unitScaling); - - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } + runData.SetField(dval*unitScaling); } else if (workStr.BeginsWith("temp:", TString::kIgnoreCase)) { - tokens = workStr.Tokenize(":("); // temp: val (units) + tokens = PStringUtils::Split(workStr.Data(), ":("); // temp: val (units) // check if expected number of tokens present - if (tokens->GetEntries() != 3) { + if (tokens.size() != 3) { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", invalid temperatue entry in header."; std::cerr << std::endl << ">> " << line.Data(); std::cerr << std::endl; @@ -3338,9 +3136,8 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() break; } // check if field value is a number - ostr = dynamic_cast(tokens->At(1)); - if (ostr->GetString().IsFloat()) { - dval = ostr->GetString().Atof(); + if (TString(tokens[1]).IsFloat()) { + dval = TString(tokens[1]).Atof(); } else { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", temperature value is not float/doulbe."; std::cerr << std::endl << ">> " << line.Data(); @@ -3348,13 +3145,7 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() success = false; break; } - runData.SetTemperature(0, dval, 0.0); - - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } + runData.SetTemperature(0, dval, 0.0); } else if (workStr.BeginsWith("setup:", TString::kIgnoreCase)) { runData.SetSetup(TString(workStr.Data()+workStr.First(":")+2)); } else if (workStr.BeginsWith("groups:", TString::kIgnoreCase)) { @@ -3377,9 +3168,9 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() break; } } else if (workStr.BeginsWith("resolution:", TString::kIgnoreCase)) { - tokens = workStr.Tokenize(":("); // resolution: val (units) + tokens = PStringUtils::Split(workStr.Data(), ":("); // resolution: val (units) // check if expected number of tokens present - if (tokens->GetEntries() != 3) { + if (tokens.size() != 3) { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", invalid time resolution entry in header."; std::cerr << std::endl << line.Data(); std::cerr << std::endl; @@ -3387,9 +3178,8 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() break; } // check if timeresolution value is a number - ostr = dynamic_cast(tokens->At(1)); - if (ostr->GetString().IsFloat()) { - dval = ostr->GetString().Atof(); + if (TString(tokens[1]).IsFloat()) { + dval = TString(tokens[1]).Atof(); } else { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", time resolution value is not float/doulbe."; std::cerr << std::endl << ">> " << line.Data(); @@ -3398,14 +3188,13 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() break; } // check units, accept (fs), (ps), (ns), (us) - ostr = dynamic_cast(tokens->At(2)); - if (ostr->GetString().Contains("fs")) + if (TString(tokens[2]).Contains("fs")) unitScaling = 1.0e-6; - else if (ostr->GetString().Contains("ps")) + else if (TString(tokens[2]).Contains("ps")) unitScaling = 1.0e-3; - else if (ostr->GetString().Contains("ns")) + else if (TString(tokens[2]).Contains("ns")) unitScaling = 1.0; - else if (ostr->GetString().Contains("us")) + else if (TString(tokens[2]).Contains("us")) unitScaling = 1.0e3; else { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", unkown time resolution units."; @@ -3414,13 +3203,7 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() success = false; break; } - runData.SetTimeResolution(dval*unitScaling); - - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } + runData.SetTimeResolution(dval*unitScaling); } else { // error std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", illegal header line."; std::cerr << std::endl; @@ -3429,9 +3212,9 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() } } else if (dataTag) { dataLineCounter++; - tokens = line.Tokenize(" ,\t"); + tokens = PStringUtils::Split(line.Data(), " ,\t"); // check if the number of data line entries is correct - if (tokens->GetEntries() != groups+1) { + if (static_cast(tokens.size()) != groups+1) { std::cerr << std::endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** found data line with a wrong data format, cannot be handled (line no " << lineNo << ")"; std::cerr << std::endl << ">> line:"; std::cerr << std::endl << ">> " << line.Data(); @@ -3439,16 +3222,9 @@ Bool_t PRunDataHandler::ReadMduAsciiFile() success = false; break; } - - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - data[i-1].push_back(ostr->GetString().Atof()); - } - - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; + + for (UInt_t i=1; i tokens = PStringUtils::Split(line.Data(), " ,\t"); // check if the number of data line entries is 2 or 3 - if ((tokens->GetEntries() != 2) && (tokens->GetEntries() != 3)) { + if ((tokens.size() != 2) && (tokens.size() != 3)) { std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** found data line with a structure different than \"x, y [, error y]\", cannot be handled (line no " << lineNo << ")"; std::cerr << std::endl; success = false; @@ -3655,35 +3429,32 @@ Bool_t PRunDataHandler::ReadAsciiFile() } // get x - ostr = dynamic_cast(tokens->At(0)); - if (!ostr->GetString().IsFloat()) { - std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": x = " << ostr->GetString().Data() << " is not a number, sorry."; + if (!TString(tokens[0]).IsFloat()) { + std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": x = " << tokens[0] << " is not a number, sorry."; std::cerr << std::endl; success = false; break; } - x = ostr->GetString().Atof(); + x = TString(tokens[0]).Atof(); // get y - ostr = dynamic_cast(tokens->At(1)); - if (!ostr->GetString().IsFloat()) { - std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": y = " << ostr->GetString().Data() << " is not a number, sorry."; + if (!TString(tokens[1]).IsFloat()) { + std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": y = " << tokens[1] << " is not a number, sorry."; std::cerr << std::endl; success = false; break; } - y = ostr->GetString().Atof(); + y = TString(tokens[1]).Atof(); // get error y if present - if (tokens->GetEntries() == 3) { - ostr = dynamic_cast(tokens->At(2)); - if (!ostr->GetString().IsFloat()) { - std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": error y = " << ostr->GetString().Data() << " is not a number, sorry."; + if (tokens.size() == 3) { + if (!TString(tokens[2]).IsFloat()) { + std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": error y = " << tokens[2] << " is not a number, sorry."; std::cerr << std::endl; success = false; break; } - ey = ostr->GetString().Atof(); + ey = TString(tokens[2]).Atof(); if (ey == 0) { std::cerr << std::endl << ">> PRunDataHandler::ReadAsciiFile **WARNING** line no " << lineNo << ": error y = 0 which doesn't make sense. Will set it to 1.0. Please check!!"; std::cerr << std::endl; @@ -3695,12 +3466,6 @@ Bool_t PRunDataHandler::ReadAsciiFile() ey = 1.0; } - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } - // keep values xVec.push_back(x); exVec.push_back(1.0); @@ -3878,8 +3643,7 @@ Bool_t PRunDataHandler::ReadDBFile() // variables needed to tokenize strings TString tstr; - TObjString *ostr; - TObjArray *tokens = nullptr; + std::vector tokens; while (!f.eof()) { // get next line from file @@ -3915,16 +3679,9 @@ Bool_t PRunDataHandler::ReadDBFile() dbTag = 4; // filter out all data tags - tokens = workStr.Tokenize(" ,\t"); - for (Int_t i=1; iGetEntries(); i++) { - ostr = dynamic_cast(tokens->At(i)); - runData.fDataNonMusr.AppendDataTag(ostr->GetString()); - } - - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; + tokens = PStringUtils::Split(workStr.Data(), " ,\t"); + for (UInt_t i=1; i(tokens->At(0)); - if (!ostr->GetString().IsFloat()) { + tokens = PStringUtils::Split(workStr.Data(), ","); + if (!TString(tokens[0]).IsFloat()) { labelledFormat = true; } else { labelledFormat = false; } - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } // prepare data vector for use PDoubleVector dummy; @@ -3990,15 +3741,13 @@ Bool_t PRunDataHandler::ReadDBFile() return false; } // split string in tokens - tokens = workStr.Tokenize(","); // line has structure: runNo,,, runTitle - ostr = dynamic_cast(tokens->At(0)); - tstr = ostr->GetString(); + tokens = PStringUtils::Split(workStr.Data(), ","); // line has structure: runNo,,, runTitle + tstr = TString(tokens[0]); if (!tstr.IsFloat()) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: runNo,,, runTitle"; std::cerr << std::endl << ">> runNo = " << tstr.Data() << ", seems to be not a number."; - delete tokens; return false; } val = tstr.Atof(); @@ -4008,35 +3757,30 @@ Bool_t PRunDataHandler::ReadDBFile() // remove all possible spaces workStr.ReplaceAll(" ", ""); // split string in tokens - tokens = workStr.Tokenize("=,"); // line has structure: tag = val,err1,err2, - if (tokens->GetEntries() < 3) { + tokens = PStringUtils::Split(workStr.Data(), "=,"); // line has structure: tag = val,err1,err2, + if (tokens.size() < 3) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\"; - delete tokens; return false; } - ostr = dynamic_cast(tokens->At(0)); - tstr = ostr->GetString(); + tstr = TString(tokens[0]); idx = GetDataTagIndex(tstr, runData.fDataNonMusr.GetDataTags()); if (idx == -1) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> data tag error: " << tstr.Data() << " seems not present in the data tag list"; - delete tokens; return false; } - switch (tokens->GetEntries()) { + switch (tokens.size()) { case 3: // tag = val,,, - ostr = dynamic_cast(tokens->At(1)); - tstr = ostr->GetString(); + tstr = TString(tokens[1]); if (!tstr.IsFloat()) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\"; std::cerr << std::endl << ">> val = " << tstr.Data() << ", seems to be not a number."; - delete tokens; return false; } val = tstr.Atof(); @@ -4046,27 +3790,23 @@ Bool_t PRunDataHandler::ReadDBFile() case 4: // tag = val,err,, case 5: // tag = val,err1,err2, // handle val - ostr = dynamic_cast(tokens->At(1)); - tstr = ostr->GetString(); + tstr = TString(tokens[1]); if (!tstr.IsFloat()) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\"; std::cerr << std::endl << ">> val = " << tstr.Data() << ", seems to be not a number."; - delete tokens; return false; } val = tstr.Atof(); runData.fDataNonMusr.AppendSubData(idx, val); // handle err1 (err2 will be ignored for the time being) - ostr = dynamic_cast(tokens->At(2)); - tstr = ostr->GetString(); + tstr = TString(tokens[2]); if (!tstr.IsFloat()) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\"; std::cerr << std::endl << ">> err1 = " << tstr.Data() << ", seems to be not a number."; - delete tokens; return false; } val = tstr.Atof(); @@ -4076,42 +3816,37 @@ Bool_t PRunDataHandler::ReadDBFile() std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\"; - delete tokens; return false; } } } else { // handle row formated data // split string in tokens - tokens = workStr.Tokenize(","); // line has structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle - if (tokens->GetEntries() != static_cast(3*runData.fDataNonMusr.GetDataTags()->size()+1)) { + tokens = PStringUtils::Split(workStr.Data(), ","); // line has structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle + if (tokens.size() != 3*runData.fDataNonMusr.GetDataTags()->size()+1) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle"; - std::cerr << std::endl << ">> found = " << tokens->GetEntries() << " tokens, however expected " << 3*runData.fDataNonMusr.GetDataTags()->size()+1; + std::cerr << std::endl << ">> found = " << tokens.size() << " tokens, however expected " << 3*runData.fDataNonMusr.GetDataTags()->size()+1; std::cerr << std::endl << ">> Perhaps there are commas without space inbetween, like 12.3,, 3.2,..."; - delete tokens; return false; } // extract data Int_t j=0; - for (Int_t i=0; iGetEntries()-1; i+=3) { + for (UInt_t i=0; i+1(tokens->At(i)); - tstr = ostr->GetString(); + tstr = TString(tokens[i]); if (!tstr.IsFloat()) { std::cerr << std::endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":"; std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle"; std::cerr << std::endl << ">> value=" << tstr.Data() << " seems not to be a number"; - delete tokens; return false; } runData.fDataNonMusr.AppendSubData(j, tstr.Atof()); // handle 1st error if present (2nd will be ignored for now) - ostr = dynamic_cast(tokens->At(i+1)); - tstr = ostr->GetString(); + tstr = TString(tokens[i+1]); if (tstr.IsWhitespace()) { runData.fDataNonMusr.AppendSubErrData(j, 1.0); } else if (tstr.IsFloat()) { @@ -4121,7 +3856,6 @@ Bool_t PRunDataHandler::ReadDBFile() std::cerr << std::endl << ">> " << workStr.Data(); std::cerr << std::endl << ">> Expected db-data line with structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle"; std::cerr << std::endl << ">> error1=" << tstr.Data() << " seems not to be a number"; - delete tokens; return false; } j++; @@ -4141,10 +3875,6 @@ Bool_t PRunDataHandler::ReadDBFile() std::cerr << std::endl << ">> number of LABELS found = " << runData.fDataNonMusr.GetLabels()->size(); std::cerr << std::endl << ">> number of Data tags found = " << runData.fDataNonMusr.GetDataTags()->size(); std::cerr << std::endl << ">> They have to be equal!!"; - if (tokens) { - delete tokens; - tokens = nullptr; - } return false; } @@ -4168,12 +3898,6 @@ Bool_t PRunDataHandler::ReadDBFile() } } - // clean up tokens - if (tokens) { - delete tokens; - tokens = nullptr; - } - // keep run name runData.SetRunName(fRunName); @@ -4218,8 +3942,7 @@ Bool_t PRunDataHandler::ReadDatFile() // variables needed to tokenize strings TString tstr; - TObjString *ostr; - TObjArray *tokens = nullptr; + std::vector tokens; UInt_t noOfDataSets = 0, noOfEntries = 0; PBoolVector isData; @@ -4239,8 +3962,8 @@ Bool_t PRunDataHandler::ReadDatFile() if (line.IsWhitespace()) continue; - tokens = line.Tokenize(" \t"); - if (tokens == nullptr) { // error + tokens = PStringUtils::Split(line.Data(), " \t"); + if (tokens.empty()) { // error std::cerr << std::endl << ">> PRunDataHandler::ReadDatFile **ERROR** couldn't tokenize the line, in lineNo: " << lineNo; std::cerr << std::endl << ">> line: '" << line << "'."; std::cerr << std::endl; @@ -4253,10 +3976,9 @@ Bool_t PRunDataHandler::ReadDatFile() // filter out all data tags: this labels are used in the msr-file to select the proper data set // for the dat-files, label and dataTag are the same - noOfEntries = tokens->GetEntries(); - for (Int_t i=0; i(tokens->At(i)); - tstr = ostr->GetString(); + noOfEntries = tokens.size(); + for (UInt_t i=0; iGetEntries() != noOfEntries) { // error - std::cerr << std::endl << ">> PRunDataHandler::ReadDatFile **ERROR** data set with wrong number of entries: " << tokens->GetEntries() << ", should be " << noOfEntries << "."; + if (tokens.size() != noOfEntries) { // error + std::cerr << std::endl << ">> PRunDataHandler::ReadDatFile **ERROR** data set with wrong number of entries: " << tokens.size() << ", should be " << noOfEntries << "."; std::cerr << std::endl << ">> in line: " << lineNo; std::cerr << std::endl << ">> line: '" << line << "'."; std::cerr << std::endl; @@ -4285,8 +4007,7 @@ Bool_t PRunDataHandler::ReadDatFile() UInt_t idx = 0; for (UInt_t i=0; i(tokens->At(i)); - tstr = ostr->GetString(); + tstr = TString(tokens[i]); if (!tstr.IsFloat()) { // make sure it is a number std::cerr << std::endl << ">> PRunDataHandler::ReadDatFile **ERROR** data set entry is not a number: " << tstr.Data(); std::cerr << std::endl << ">> in line: " << lineNo; @@ -4310,11 +4031,6 @@ Bool_t PRunDataHandler::ReadDatFile() } } } - // cleanup - if (tokens) { - delete tokens; - tokens = nullptr; - } } f.close(); @@ -6636,8 +6352,6 @@ TString PRunDataHandler::FileNameFromTemplate(TString &fileNameTemplate, Int_t r { TString result(""); - TObjArray *tok=nullptr; - TObjString *ostr; TString str; // check year string @@ -6660,19 +6374,18 @@ TString PRunDataHandler::FileNameFromTemplate(TString &fileNameTemplate, Int_t r } // tokenize template string - tok = fileNameTemplate.Tokenize("[]"); - if (tok == nullptr) { + std::vector tok = PStringUtils::Split(fileNameTemplate.Data(), "[]"); + if (tok.empty()) { std::cerr << std::endl << ">> PRunDataHandler::FileNameFromTemplate: **ERROR** couldn't tokenize template!" << std::endl; return result; } - if (tok->GetEntries()==1) { + if (tok.size()==1) { std::cerr << std::endl << ">> PRunDataHandler::FileNameFromTemplate: **WARNING** template without tags." << std::endl; } // go through the tokens and generate the result string - for (Int_t i=0; iGetEntries(); i++) { - ostr = dynamic_cast(tok->At(i)); - str = ostr->GetString(); + for (UInt_t i=0; i Date: Sat, 6 Jun 2026 16:59:58 +0200 Subject: [PATCH 23/24] musrFT: replace TObjArray/TObjString with PStringUtils Co-Authored-By: Claude Opus 4.8 --- src/musrFT.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/musrFT.cpp b/src/musrFT.cpp index e55855ff..ae8f9e5f 100644 --- a/src/musrFT.cpp +++ b/src/musrFT.cpp @@ -36,13 +36,12 @@ #include #include #include +#include #include #include #include #include -#include -#include #include #ifdef HAVE_GIT_REV_H @@ -50,6 +49,7 @@ #endif #include "PMusr.h" +#include "PStringUtils.h" #include "PStartupHandler.h" #include "PMsrHandler.h" #include "PRunDataHandler.h" @@ -234,17 +234,15 @@ Bool_t musrFT_filter_histo(Int_t &i, Int_t argc, Char_t *argv[], musrFT_startup_ return false; } } else { // should be something like h0-hN with h0, hN numbers - TObjArray *tok = tstr.Tokenize("-"); - if (tok->GetEntries() != 2) { + std::vector tok = PStringUtils::Split(tstr.Data(), "-"); + if (tok.size() != 2) { std::cerr << std::endl << ">> musrFT **ERROR** found --histo argument '" << tstr << "' which is not of the form -." << std::endl; startupParam.histo.clear(); return false; } - TObjString *ostr; TString sstr(""); Int_t first=0, last=0; - ostr = dynamic_cast(tok->At(0)); - sstr = ostr->GetString(); + sstr = tok[0]; if (sstr.IsDigit()) { first = sstr.Atoi(); } else { @@ -253,8 +251,7 @@ Bool_t musrFT_filter_histo(Int_t &i, Int_t argc, Char_t *argv[], musrFT_startup_ startupParam.histo.clear(); return false; } - ostr = dynamic_cast(tok->At(1)); - sstr = ostr->GetString(); + sstr = tok[1]; if (sstr.IsDigit()) { last = sstr.Atoi(); } else { @@ -273,10 +270,6 @@ Bool_t musrFT_filter_histo(Int_t &i, Int_t argc, Char_t *argv[], musrFT_startup_ for (Int_t k=first; k<=last; k++) { startupParam.histo.push_back(k); } - - // clean up - if (tok) - delete tok; } } From f2ab8dbeb9020c9ab7ed3ff8faedf601efd493d0 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 6 Jun 2026 17:52:59 +0200 Subject: [PATCH 24/24] add missing path to CMakeLists.txt. --- src/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e2d2936..0fc2ef82 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -76,6 +76,10 @@ target_include_directories(msr2data target_link_libraries(msr2data ${ROOT_LIBRARIES} ${MUSRFIT_LIBS}) add_executable(msr2msr msr2msr.cpp classes/PStringUtils.cpp) +target_include_directories(msr2msr + BEFORE PRIVATE + $ +) target_link_libraries(msr2msr ${ROOT_LIBRARIES}) add_executable(musrfit musrfit.cpp)