LCOV - code coverage report
Current view: top level - include/utils - MooseStringUtils.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 7323e9 Lines: 56 56 100.0 %
Date: 2025-11-05 20:01:15 Functions: 63 115 54.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include <algorithm>
      13             : #include <sstream>
      14             : #include <string>
      15             : #include <vector>
      16             : 
      17             : // The capabilities library (capabilities target in moose.mk) uses this
      18             : // utility for parsing. We don't want to include libmesh libraries in
      19             : // this library as the test harness uses it. However, in the convert
      20             : // method (heavily used by the parser), we really want to take advantage
      21             : // of libMesh::demangle() for useful error messages. So this lets us
      22             : // still use pretty demangling when used by MOOSE but not by the
      23             : // capabilities library (which is probably ok...)
      24             : #ifndef MOOSESTRINGUTILS_NO_LIBMESH
      25             : #include "libmesh/libmesh_common.h"
      26             : #endif
      27             : 
      28             : /*
      29             :  * This must stay a header-only utility! It is used in the capabilities python module and
      30             :  * we do not want to link against any MOOSE libs.
      31             :  */
      32             : namespace MooseUtils
      33             : {
      34             : /**
      35             :  * Standard scripting language trim function
      36             :  */
      37             : inline std::string
      38   791816831 : trim(const std::string & str, const std::string & white_space = " \t\n\v\f\r")
      39             : {
      40   791816831 :   const auto begin = str.find_first_not_of(white_space);
      41   791816831 :   if (begin == std::string::npos)
      42   193558544 :     return ""; // no content
      43   695037559 :   const auto end = str.find_last_not_of(white_space);
      44   695037559 :   return str.substr(begin, end - begin + 1);
      45             : }
      46             : 
      47             : /**
      48             :  * This function will split the passed in string on a set of delimiters appending the substrings
      49             :  * to the passed in vector.  The delimiters default to "/" but may be supplied as well.  In
      50             :  * addition if min_len is supplied, the minimum token length will be >= than the supplied
      51             :  * value. T should be std::string or a MOOSE derived string class.
      52             :  */
      53             : template <typename T>
      54             : void
      55   803454968 : tokenize(const std::string & str,
      56             :          std::vector<T> & elements,
      57             :          unsigned int min_len = 1,
      58             :          const std::string & delims = "/")
      59             : {
      60   803454968 :   elements.clear();
      61             : 
      62   803454968 :   std::string::size_type last_pos = str.find_first_not_of(delims, 0);
      63   803454968 :   std::string::size_type pos = str.find_first_of(delims, std::min(last_pos + min_len, str.size()));
      64             : 
      65  1516811329 :   while (last_pos != std::string::npos)
      66             :   {
      67  1512056532 :     elements.push_back(str.substr(last_pos, pos - last_pos));
      68             :     // skip delims between tokens
      69  1512056532 :     last_pos = str.find_first_not_of(delims, pos);
      70  1512056532 :     if (last_pos == std::string::npos)
      71   798700171 :       break;
      72   713356361 :     pos = str.find_first_of(delims, std::min(last_pos + min_len, str.size()));
      73             :   }
      74   803454968 : }
      75             : 
      76             : /**
      77             :  * Takes the string representation of a value and converts it to the value.
      78             :  *
      79             :  * For standard numeric types, this gets around the deficiencies in the STL
      80             :  * stoi and stod methods where they might successfully convert part of a string
      81             :  * to a number when we'd instead prefer to get a failure.
      82             :  *
      83             :  * For string and string-derived types, this does a direct copy and does
      84             :  * not utilize a stream.
      85             :  *
      86             :  * For all other types, this uses the stringstream >> operator to fill
      87             :  * the value.
      88             :  *
      89             :  * @param str The string to convert from
      90             :  * @param value The typed value to fill
      91             :  * @param throw_on_failure If true, throw a std::invalid_argument on failure
      92             :  * @return Whether or not the conversion succeeded
      93             :  */
      94             : template <class T>
      95             : bool
      96     1008992 : convert(const std::string & str, T & value, const bool throw_on_failure)
      97             : {
      98             :   // Special case for numeric values, also handling range checking
      99             :   if constexpr (std::is_same_v<short int, T> || std::is_same_v<unsigned short int, T> ||
     100             :                 std::is_same_v<int, T> || std::is_same_v<unsigned int, T> ||
     101             :                 std::is_same_v<long int, T> || std::is_same_v<unsigned long int, T> ||
     102             :                 std::is_same_v<long long int, T> || std::is_same_v<unsigned long long int, T>)
     103             :   {
     104             :     // Try read a double and try to cast it to an int
     105             :     long double double_val;
     106      266658 :     std::stringstream double_ss(str);
     107      266658 :     double_ss >> double_val;
     108             : 
     109      266658 :     if (!double_ss.fail() && double_ss.eof())
     110             :     {
     111             :       // on arm64 the long double does not have sufficient precision
     112      266129 :       std::stringstream int_ss(str);
     113      266129 :       const bool use_int = !(int_ss >> value).fail() && int_ss.eof();
     114             : 
     115             :       // Check to see if it's an integer and thus within range of an integer
     116      266129 :       if (double_val == static_cast<long double>(static_cast<T>(double_val)))
     117             :       {
     118      266093 :         if (!use_int)
     119          14 :           value = static_cast<T>(double_val);
     120      266093 :         return true;
     121             :       }
     122      266129 :     }
     123      266658 :   }
     124             :   // Non numeric values
     125             :   else
     126             :   {
     127             :     // string or derived string: direct copy
     128             :     if constexpr (std::is_base_of_v<std::string, T>)
     129             :     {
     130       13189 :       value = str;
     131       13189 :       return true;
     132             :     }
     133             :     // non-string or numeric, use stringstream >>
     134             :     else
     135             :     {
     136     1458290 :       std::stringstream ss(trim(str));
     137      729145 :       if (!(ss >> value).fail() && ss.eof())
     138      640319 :         return true;
     139      729145 :     }
     140             :   }
     141             : 
     142       89391 :   if (throw_on_failure)
     143             :   {
     144         549 :     std::string error = "Unable to convert '" + str + "' to type ";
     145             : #ifdef MOOSESTRINGUTILS_NO_LIBMESH
     146             :     error += typeid(T).name();
     147             : #else
     148         549 :     error += libMesh::demangle(typeid(T).name());
     149             : #endif
     150         549 :     throw std::invalid_argument(error);
     151         549 :   }
     152             : 
     153       88842 :   return false;
     154             : }
     155             : 
     156             : /**
     157             :  *  tokenizeAndConvert splits a string using delimiter and then converts to type T.
     158             :  *  If the conversion fails tokenizeAndConvert returns false, otherwise true.
     159             :  */
     160             : template <typename T>
     161             : bool
     162      176505 : tokenizeAndConvert(const std::string & str,
     163             :                    std::vector<T> & tokenized_vector,
     164             :                    const std::string & delimiter = " \t\n\v\f\r")
     165             : {
     166      176505 :   std::vector<std::string> tokens;
     167      176505 :   MooseUtils::tokenize(str, tokens, 1, delimiter);
     168      176505 :   tokenized_vector.resize(tokens.size());
     169      510913 :   for (std::size_t i = 0; i < tokens.size(); ++i)
     170      334682 :     if (!convert<T>(tokens[i], tokenized_vector[i], false))
     171         274 :       return false;
     172      176231 :   return true;
     173      176505 : }
     174             : 
     175             : /**
     176             :  * Convert supplied string to upper case.
     177             :  * @params name The string to convert upper case.
     178             :  */
     179             : inline std::string
     180  1192385883 : toUpper(std::string name)
     181             : {
     182  1192385883 :   std::transform(name.begin(), name.end(), name.begin(), ::toupper);
     183  1192385883 :   return name;
     184             : }
     185             : 
     186             : /**
     187             :  * Convert supplied string to lower case.
     188             :  * @params name The string to convert upper case.
     189             :  */
     190             : inline std::string
     191     3857918 : toLower(std::string name)
     192             : {
     193     3857918 :   std::transform(name.begin(), name.end(), name.begin(), ::tolower);
     194     3857918 :   return name;
     195             : }
     196             : }

Generated by: LCOV version 1.14