Itasca C++ Interface
Loading...
Searching...
No Matches
basestring.h
Go to the documentation of this file.
1#pragma once
7// MOO NOTE: a LOT LOT Of these functions should probably be changed to take a
8// string_view argument now that string will automatically convert....
9// also standardize on whether we use
10// caseInsensitiveFUNC() or
11// FUNC(...,bool caseSensitive=false) as a standard.
12// I'm leaning to the latter for conciseness
13#include "basedef.h"
14#include "to.h"
15#include <cctype>
16#include <cwctype>
17#include <format>
18#include <string>
19#include <vector>
20#include <string_view>
21#ifdef _WIN32
22#include <codeanalysis\warnings.h>
23#endif
24
25// Global namespace versions of std strings
26using wstring = std::wstring; // Old string class to avoid interface compatibility breaks
27using string = std::string; // Bring into global namespace like the other base types (uint32, etc)
28using string_view = std::string_view;
29//using String = std::wstring; // DEPRECATED - should not be used in new code.
30
31// String conversion routines.
32// These return a tuple, the first is the converted value and the second is whether the conversion was successful.
33// If not successful the first value is a default construction.
34BASE_EXPORT std::tuple<int32,bool> isInt32(const string_view &in);
35BASE_EXPORT std::tuple<uint32,bool> isUInt32(const string_view &in);
36BASE_EXPORT std::tuple<int64,bool> isInt64(const string_view &in);
37BASE_EXPORT std::tuple<uint64,bool> isUInt64(const string_view &in);
38BASE_EXPORT std::tuple<double,bool> isDouble(const string_view &in);
39BASE_EXPORT std::tuple<bool,bool> isBool(const string_view &in,const string_view &out="on,off,true,false,yes,no");
40
41// explicit string versions to avoid ambiguity
42inline std::tuple<int32,bool> isInt32(const string &in) { return isInt32(string_view(in)); }
43inline std::tuple<uint32,bool> isUInt32(const string &in) { return isUInt32(string_view(in)); }
44inline std::tuple<int64,bool> isInt64(const string &in) { return isInt64(string_view(in)); }
45inline std::tuple<uint64,bool> isUInt64(const string &in) { return isUInt64(string_view(in)); }
46inline std::tuple<double,bool> isDouble(const string &in) { return isDouble(string_view(in)); }
47inline std::tuple<bool,bool> isBool(const string &in,const string_view &out="on,off,true,false,yes,no") { return isBool(string_view(in),out); }
48inline std::tuple<int32,bool> isInt32v(const string_view &in) { return isInt32(string_view(in)); }
49inline std::tuple<uint32,bool> isUInt32v(const string_view &in) { return isUInt32(string_view(in)); }
50inline std::tuple<int64,bool> isInt64v(const string_view &in) { return isInt64(string_view(in)); }
51inline std::tuple<uint64,bool> isUInt64v(const string_view &in) { return isUInt64(string_view(in)); }
52inline std::tuple<double,bool> isDoublev(const string_view &in) { return isDouble(string_view(in)); }
53inline std::tuple<bool,bool> isBoolv(const string_view &in,const string_view &out="on,off,true,false,yes,no") { return isBool(string_view(in),out); }
54
55// String conversion routines - converts string directly, throwing out the success check.
56// No exception is thrown on failure - just returns a default constructed value.
57// This is a common template used just to avoid code repetition in the type-specific version
58template <typename T,typename ... Args>
59T toStringConv(const string_view &in,Args...args,bool te, std::tuple<T,bool> (*f)(const string_view &,Args...));
60inline int32 toInt32(const string_view &in, bool throwException = false) { return toStringConv<int32>(in, throwException, isInt32v); }
61inline uint32 toUInt32(const string_view &in,bool throwException=false) { return toStringConv<uint32>(in, throwException, isUInt32v); }
62inline int64 toInt64(const string_view &in,bool throwException=false) { return toStringConv<int64>(in, throwException, isInt64v); }
63inline uint64 toUInt64(const string_view &in,bool throwException=false) { return toStringConv<uint64>(in, throwException, isUInt64v); }
64inline double toDouble(const string_view &in,bool throwException=false) { return toStringConv<double>(in, throwException, isDoublev); }
65inline bool toBool(const string_view &in, const string_view &out = "on,off,true,false,yes,no", bool throwException = false) { return toStringConv<bool,const string_view &>(in, out, throwException, isBool); }
66template <typename ... Args>
67string rformat(const string &s,Args...args) { return std::vformat(s,std::make_format_args(args...)); }
68
69
70// vector of strings - for convenience and to add some handy utility functions.
71class StringList : public std::vector<string> {
72 public:
73 using std::vector<string>::vector;
74 enum class Empty { Keep, Skip };
75
76 StringList() {}
77 StringList(std::initializer_list<string> list) : std::vector<string>(list) {}
78 StringList(const StringList &s) : std::vector<string>(s) {}
79 StringList(const std::vector<string> &v) : std::vector<string>(v) {}
80 StringList(StringList &&v) noexcept : std::vector<string>(std::move(v)) {}
81 StringList(std::vector<string> &&v) noexcept : std::vector<string>(std::move(v)) {}
82 explicit StringList(const string_view &s) : std::vector<string>{string(s)} { }
83
84 const StringList &operator=(const StringList &in) { std::vector<string>::operator=(in); return *this; }
85
86 const StringList &operator+=(const string &s) { push_back(s); return *this; }
87 const StringList &operator+=(const std::vector<string> &v) { for (auto &s : v) push_back(s); return *this; }
88 const StringList &operator<<(const string &s) { return operator+=(s); }
89 const StringList &operator<<(const StringList &s) { return operator+=(s); }
90 StringList operator+(const StringList &s) const { StringList ret(*this); ret += s; return ret; }
91
92};
93BASE_EXPORT std::vector<string> toStringList(const std::vector<string_view> &v);
94BASE_EXPORT bool contains(const std::vector<string> &all, const string &s2,bool caseSensitivity=false);
95BASE_EXPORT size_t find(const StringList &s,const string &s2,bool caseSensitive=false);
96BASE_EXPORT size_t remove(StringList *s,const string_view &s2,bool caseSensitive=false);
97BASE_EXPORT string::size_type findRegex(const StringList &s,const string_view &regex,string::size_type start=0); // NOTE Case insensitive
98BASE_EXPORT StringList removeDuplicates(const StringList &s);
99
100// String utility functions
101// Codec conversion
102[[nodiscard]] BASE_EXPORT string tostring(const std::wstring &s); // Converts wstring to string
103[[nodiscard]] BASE_EXPORT string tostring(const std::u16string &s);
104[[nodiscard]] BASE_EXPORT string tostring(const std::u32string &s);
105[[nodiscard]] inline string tostring(const string_view &s) { return string(s.data(),s.size()); }
106[[nodiscard]] inline string tostring(const char *s) { return string(s); }
107[[nodiscard]] inline string tostring(const string & s) { return s; } // For template support
108[[nodiscard]] BASE_EXPORT std::wstring towstring(const string &s); // Converts string ot wstring
109[[nodiscard]] BASE_EXPORT std::wstring towstring(const std::u16string &s);
110[[nodiscard]] BASE_EXPORT std::u16string tou16string(const string &s); // Converts string to u16string
111[[nodiscard]] BASE_EXPORT std::u16string tou16string(const std::wstring &s); // Converts wstring to u16string
112[[nodiscard]] BASE_EXPORT std::u16string tou16string(const string_view &s); // Converts string to u32string
113[[nodiscard]] BASE_EXPORT std::u32string tou32string(const string &s); // Converts string to u32string
114[[nodiscard]] BASE_EXPORT std::u32string tou32string(const string_view &s); // Converts string to u32string
115[[nodiscard]] BASE_EXPORT string toBase64(const std::vector<char> &in);
116[[nodiscard]] BASE_EXPORT std::vector<char> fromBase64(const string &in);
117
118// Capitalization
119[[nodiscard]] BASE_EXPORT string toUpper(const string_view &s); // All upper case
120[[nodiscard]] BASE_EXPORT string toLower(const string_view &s); // All lower case
121[[nodiscard]] BASE_EXPORT string capitalizeFirstLetter(const string_view &s);
122[[nodiscard]] BASE_EXPORT uint64 caseInsensitiveHash(const string_view &s);
123
124// StringList - join and split
125template <typename T,typename U> [[nodiscard]] T join(const std::vector<T> &s,const U &sep); // Note works on StringList
126[[nodiscard]] BASE_EXPORT std::vector<string_view> splitView(const string_view &s, const string_view &sep,bool keepEmptyParts=false); // NOTE case insensitive
127[[nodiscard]] inline std::vector<string> split(const string_view &s, const string_view &sep,bool keepEmptyParts=false) { return toStringList(splitView(s,sep,keepEmptyParts)); }
128[[nodiscard]] BASE_EXPORT std::vector<string_view> splitViewRegex(const string_view &s,const string_view &regex,bool keepEmptyParts=false); // NOTE Case insensitive
129[[nodiscard]] inline std::vector<string> splitRegex(const string_view &s,const string_view &regex,bool keepEmptyParts=false) { return toStringList(splitViewRegex(s,regex,keepEmptyParts)); }
130
131// Matching and regular expression
132[[nodiscard]] BASE_EXPORT string_view matchViewRegex(const string_view &s,const string_view &regex,string::size_type start=0); // NOTE Case insensitive
133[[nodiscard]] inline string matchRegex(const string_view &s,const string_view &regex,string::size_type start=0) { return tostring(matchViewRegex(s,regex,start)); }
134[[nodiscard]] BASE_EXPORT string replaceRegex(const string &s,const string_view &regex,const string &after); // NOTE Case insensitive
135[[nodiscard]] BASE_EXPORT string::size_type find(const string_view &s1,const string_view &s2,string::size_type start=0,bool caseSensitive=false);
136[[nodiscard]] BASE_EXPORT string::size_type findRegex(const string_view &s,const string_view &regex,string::size_type start=0); // NOTE Case insensitive
137[[nodiscard]] BASE_EXPORT bool exactMatchRegex(const string_view &s,const string_view &regex); // NOTE Case insensitive
138
139// Modify String
140[[nodiscard]] BASE_EXPORT string_view trimmed_view(const string_view &s); // Removes whitespace on front and back.
141[[nodiscard]] inline string trimmed(const string_view &s) { return string(trimmed_view(s)); }
142[[nodiscard]] BASE_EXPORT string simplified(const string_view &s); // As trimmed, but also reduces whitespace sequences inside to one space.
143[[nodiscard]] BASE_EXPORT string replace(string s, const string_view &sub,const string_view &newsub,bool caseSensitive=false); // Replace all instances of sub wiwth newsub in s
144[[nodiscard]] BASE_EXPORT string cleanupTypename(const char *name);
145[[nodiscard]] BASE_EXPORT string remove(string s,char c);
146[[nodiscard]] inline string remove(string s,const string_view &sub,bool caseSensitive=false) { return replace(s,sub,{},caseSensitive); }
147[[nodiscard]] BASE_EXPORT string remove(string s,string::size_type start,string::size_type len);
148[[nodiscard]] inline string_view chopped(const string_view &s,string::size_type i) { if (i>=s.size()) return {}; return s.substr(0,s.length()-i); }
149[[nodiscard]] BASE_EXPORT string clip(const string_view &s,string::size_type n); // Handy method that truncates the string to size (length-3) and appends "..." to get a final size of length (does nothing if the initial string size is less than parameter length). length must be at least equal to 3
150[[nodiscard]] BASE_EXPORT string enquoteString(const string &s,char quote='"'); // Converts the string This is a "test" to "This ia a \"test\""
151[[nodiscard]] BASE_EXPORT string_view substr(const string_view &s,string::size_type pos,string::size_type count=string::npos); // Same as s.substr() except returns empty string if pos > size()
152[[nodiscard]] inline string_view right(const string_view &s,string::size_type num) { return num >= s.length() ? s : s.substr(s.length()-num,num); }
153[[nodiscard]] BASE_EXPORT string buildRandom(string::size_type length);
154
155// Checking - NOTE
156[[nodiscard]] BASE_EXPORT int32 compare(const string_view &s1, const string_view &s2,bool caseSensitive=false);
157[[nodiscard]] inline bool equal(const string_view &s,const string_view &c,bool caseSensitive=false) { return compare(s,c,caseSensitive)==0; } // Handy shorthand
158[[nodiscard]] BASE_EXPORT bool startsWith(const string_view &in,const string_view &check,bool caseSensitive = false);
159[[nodiscard]] BASE_EXPORT bool endsWith(const string_view &in, const string_view &check,bool caseSensitive = false);
160[[nodiscard]] BASE_EXPORT bool contains(const string_view &in,const string_view &check,bool caseSensitive = false);
161[[nodiscard]] inline bool containsRegex(const string_view &in,const string_view &regex) { return findRegex(in,regex)!=string::npos; }
162[[nodiscard]] BASE_EXPORT bool wildcardMatch(const string_view &patter,const string_view &test);
163[[nodiscard]] BASE_EXPORT uint64 firstNonBlankCharacter(const string_view &s);
164
165// Convert number of bytes to string with postfix (b=bytes, Kb=kilobytes, etc.)
166[[nodiscard]] BASE_EXPORT string formatBytes(uint64 bytes,uint32 precision=3);
167
168
169// A buffer class - thin wrapper around std::vector<char>
170// Useful in places where we would other use QByteArray.
171class Buffer : public std::vector<char> {
172 public:
173 using std::vector<char>::vector;
174 Buffer(const std::vector<char> &v) : std::vector<char>(v) {}
175 Buffer(std::vector<char> &&v) : std::vector<char>(std::move(v)) {}
176 Buffer(const char *c, size_t len) : std::vector<char>(c, c+len) {}
177 explicit Buffer(const string &s) : std::vector<char>(s.begin(),s.end()) {}
178
179 const Buffer &operator=(const std::vector<char> &v) { std::vector<char>::operator=(v); return *this; }
180 const Buffer &operator=(std::vector<char> &&v) { std::vector<char>::operator=(std::move(v)); return *this; }
181
182 void append(const char *data, uint64 len) { insert(end(),data, data+len); }
183 void operator+=(const string &s) { insert(end(),s.begin(),s.end()); }
184 void operator+=(const Buffer &s) { insert(end(),s.begin(),s.end()); }
185 string toString() const { return string(begin(), end()); }
186 string_view toStringView() const { return string_view(begin(),end()); }
187 const char *constData() const { return data(); }
188};
189
191//*
192//* Specifically, compares to length of token or keyword, whichever is shorter.\n
193//* If token is longer than keyword no match.\n
194//* If keyword contains the character '^', that character is disregarded for comparison purposes
195//* and token must have at least as many characters as precede the '^' character.\n
196//* If a token has a starting hyphen '-', then it is ignored for matching.
197//* If a token has an internal hyphen 'one-two', then the hyphen must be present in keyword and
198//* BOTH sides are checked using matching rules and any possible '^' character.
199//*/
200BASE_EXPORT bool match(const string &keyword,const string &token,bool forceAbbreviationsAllowed=false);
201BASE_EXPORT uint32 match(const StringList &keywords,const string &token,bool forceAbbreviationsAllowed=false); // 0 if no match, base 1 if match (ugh)
202BASE_EXPORT void matchSynonymsAllowed(bool b);
203BASE_EXPORT bool matchSynonymsAllowed();
204// Getkeyword pull the FIRST full alias keyword from the keyword list at the given index, or def if it doesn't exist
205BASE_EXPORT string getKeyword(const StringList &keywords,const uint64 index,const string &def={});
206inline string getKeyword(const string &keywords,const uint64 index,const string &def={}) { return getKeyword(split(keywords,","),index,def); }
207
208// Create a std::format specification using the parameters indicated {:fw.pn}
209// Note position indicator is not included (yet).
210BASE_EXPORT string buildFormat(int64 width, char notation='\0', int precision=-1,char fill=' ');
211
212namespace base {
213 // These are type->string conversion functions, for convenience. less verbose than std::format
214 // and intended for use in formatted << output, but can be used in any string stuff.
215 // ts stands for To String, fs stands for From String.
216 // If you use these in text output I reccomend you put "using base::ts" at the start of the FUNCTION.
217 template <class T>
218 inline string ts(const T &t, int width=0, char notation = '\0', int precision = -1, char fill = ' ');
219
220
221 // Concise conversion from string to type. fsTest returns a tuple with the second
222 // value being a success boolean. fs just returns the type with an optional
223 // second argument indicating if an exception should be thrown on failure.
224 template <typename T>
225 std::tuple<T, bool> fsTest(const string_view &) { static_assert(sizeof(T{})==0); return {T{},false}; }
226 template<> inline std::tuple<int32, bool> fsTest(const string_view &in) { return isInt32v(in); }
227 template<> inline std::tuple<uint32, bool> fsTest(const string_view &in) { return isUInt32v(in); }
228 template<> inline std::tuple<int64, bool> fsTest(const string_view &in) { return isInt64v(in); }
229 template<> inline std::tuple<uint64, bool> fsTest(const string_view &in) { return isUInt64v(in); }
230 template<> inline std::tuple<double, bool> fsTest(const string_view &in) { return isDoublev(in); }
231 template<> inline std::tuple<bool, bool> fsTest(const string_view &in) { return isBoolv(in); }
232 template<> inline std::tuple<string, bool> fsTest(const string_view &in) { return {string(in),true}; }
233
234 template <typename T> T fs(const string_view &, [[maybe_unused]] bool throwException = false) { static_assert(sizeof(T{})==0); return T{}; }
235 template<> inline int32 fs(const string_view &in,bool throwException) { return toInt32(in,throwException); }
236 template<> inline uint32 fs(const string_view &in,bool throwException) { return toUInt32(in,throwException); }
237 template<> inline int64 fs(const string_view &in,bool throwException) { return toInt64(in,throwException); }
238 template<> inline uint64 fs(const string_view &in,bool throwException) { return toUInt64(in,throwException); }
239 template<> inline double fs(const string_view &in,bool throwException) { return toDouble(in,throwException); }
240 template<> inline bool fs(const string_view &in,bool throwException) { return toBool(in,"on,off,true,false,yes,no",throwException); }
241 template<> inline string fs(const string_view &in,bool) { return {string(in),true}; }
242}
243
244// < and == functors for strings - to use in std containers when CI comparisons are wanted!
246 public:
247 bool operator()(const string_view &left, const string_view &right) const { return compare(left,right) < 0; }
248};
250 public:
251 bool operator()(const string_view &left, const string_view &right) const { return compare(left,right) == 0; }
252};
254 public:
255 uint64 operator()(const string_view &in) const { return caseInsensitiveHash(in); }
256};
257
258//----------------------------------------------------
259// Implementations
260//----------------------------------------------------
261
262// Might want to move this to a dedicated header so not everybody has to include these containers
263
264#include <map>
265#include <unordered_map>
266#include <set>
267#include <unordered_set>
268
269template <typename T> using StringMap = std::map<string, T, StringCILess>;
270
271template <typename T> using StringMultiMap = std::multimap<string, T, StringCILess>;
272
273template <typename T> using StringHashMap = std::unordered_map<string, T, StringCIHash, StringCIEqual>;
274
275using StringSet = std::set<string, StringCILess>;
276
277using StringHashSet = std::unordered_set<string, StringCIHash, StringCIEqual>;
278
279template <class T>
280inline string base::ts(const T &t, int width, char, int , char fill) {
281 string s = rformat(buildFormat(width, '\0', -1, fill), t);
282 if (width!=0)
283 return clip(s,std::abs(width));
284 return s;
285}
286
287template <>
288inline string base::ts(const string &s,int width,char,int,char fill) {
289 string ret = rformat(buildFormat(width,'\0',-1,fill),s);
290 if (width!=0)
291 return clip(ret,std::abs(width));
292 return ret;
293}
294
295// OK the fmt;:format library will output -0.0000 instead of 0.0000 if the value was
296// <0 but below limits. This is confusing and breaks former output checks.
297// So we need a template override to check for that and get rid of it (sigh)
298template <>
299inline string base::ts(const double &dIn, int width, char notation, int precision, char fill) {
300 double d(dIn);
301 if (d==0.0) // Resolve -0
302 d = 0.0;
303 return rformat(buildFormat(width, notation, precision, fill),d);
304}
305
306template <>
307inline string base::ts(const float &dIn, int width, char notation, int precision, char fill) {
308 float d(dIn);
309 if (d==0.0) // Resolve -0
310 d = 0.0;
311 return rformat(buildFormat(width, notation, precision, fill),d);
312}
313template <typename T,typename U>
314T join(const std::vector<T> &s,const U &sep) {
315 string ret;
316 for (StringList::size_type i=0;i<s.size();++i) {
317 if (i) ret += sep;
318 ret += s[i];
319 }
320 return ret;
321}
322
323// For the model information system (MOO NOTE: changed to class so we can use forward declaration)
324using StateInfoOutput = StringMap<StringList>;
325
326//----------------------------------------------------
327// Implementations
328//----------------------------------------------------
329
330template <typename T,typename ... Args>
331T toStringConv(const string_view &in,Args...args,bool te, std::tuple<T,bool> (*f)(const string_view &,Args...)) {
332 auto [val, ok] = f(in,args...);
333 if (te==true && ok==false)
334#ifdef __LINUX
335 throw std::runtime_error("String conversion error.");
336#else
337 throw std::exception("String conversion error.");
338#endif
339 return val;
340}
341
342template <class ... Args>
343struct FormatCheck : public std::format_string<std::type_identity_t<Args>...> {
344 template <unsigned N>
345 consteval FormatCheck(const char (&s)[N]) : std::format_string<std::type_identity_t<Args>...>(s) {
346 for (auto i=&(s[0]);i!=&(s[N]);++i) {
347 if (*i=='\\') { // Check for \ escape
348 ++i;
349 if (i==&(s[N])) break;
350 } else if (*i=='%') { // Check for %
351 ++i;
352 if (i==&(s[N]))
353 break;
354 if (*i >= '1' and *i <= '9')
355 assert(0 && "%1, %2 etc. token replacement is no longer supported. Use std::format {} syntax.");
356 }
357 }
358 }
359};
360
361// This allows you to send StringLists to a std::format
362template <>
363struct std::formatter<StringList> : public std::formatter<string> {
364 template <typename ParseContext>
365 constexpr auto parse(ParseContext &ctx) { return std::formatter<string>::parse(ctx); }
366
367 template <typename FormatContext>
368 constexpr auto format(StringList const &val, FormatContext &ctx) const {
369 return std::formatter<string>::format(join(val,","), ctx);
370 }
371};
372
373// EoF
Base type definitions for the engine.
Definition basestring.h:171
Definition basestring.h:71
BASE_EXPORT std::tuple< double, bool > isDouble(const string_view &in)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition basestring.cpp:61
BASE_EXPORT std::tuple< uint64, bool > isUInt64(const string_view &in)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition basestring.cpp:52
BASE_EXPORT std::tuple< uint32, bool > isUInt32(const string_view &in)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition basestring.cpp:34
#define BASE_EXPORT
Definition basedef.h:25
BASE_EXPORT bool match(const string &keyword, const string &token, bool forceAbbreviationsAllowed=false)
‍**
Definition basestring.cpp:521
BASE_EXPORT std::tuple< int64, bool > isInt64(const string_view &in)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition basestring.cpp:43
Definition basestring.h:343
Definition basestring.h:249
Definition basestring.h:253
Definition basestring.h:245
A overflow checked shorthand for static_cast<T>().