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 : }
|