TrinityCore
StringConvert.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef TRINITY_STRINGCONVERT_H
19#define TRINITY_STRINGCONVERT_H
20
21#include "Define.h"
22#include "Errors.h"
23#include "Optional.h"
24#include "Types.h"
25#include "Util.h"
26#include <charconv>
27#include <string>
28#include <string_view>
29#include <type_traits>
30
32{
33 template <typename T, typename = void> struct For
34 {
35 static_assert(Trinity::dependant_false_v<T>, "Unsupported type used for ToString or StringTo");
36 /*
37 static Optional<T> FromString(std::string_view str, ...);
38 static std::string ToString(T&& val, ...);
39 */
40 };
41
42 template <typename T>
43 struct For<T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>>>
44 {
45 static Optional<T> FromString(std::string_view str, int base = 10)
46 {
47 if (base == 0)
48 {
49 if (StringEqualI(str.substr(0, 2), "0x"))
50 {
51 base = 16;
52 str.remove_prefix(2);
53 }
54 else if (StringEqualI(str.substr(0, 2), "0b"))
55 {
56 base = 2;
57 str.remove_prefix(2);
58 }
59 else
60 base = 10;
61
62 if (str.empty())
63 return std::nullopt;
64 }
65
66 char const* const start = str.data();
67 char const* const end = (start + str.length());
68
69 T val;
70 std::from_chars_result const res = std::from_chars(start, end, val, base);
71 if ((res.ptr == end) && (res.ec == std::errc()))
72 return val;
73 else
74 return std::nullopt;
75 }
76
77 static std::string ToString(T val)
78 {
79 using buffer_size = std::integral_constant<size_t, sizeof(T) < 8 ? 11 : 20>;
80
81 std::string buf(buffer_size::value,'\0'); /* 2^64 is 20 decimal characters, -(2^63) is 20 including the sign */
82 char* const start = buf.data();
83 char* const end = (start + buf.length());
84 std::to_chars_result const res = std::to_chars(start, end, val);
85 ASSERT(res.ec == std::errc());
86 buf.resize(res.ptr - start);
87 return buf;
88 }
89 };
90
91#ifdef TRINITY_NEED_CHARCONV_WORKAROUND
92 /*
93 If this is defined, std::from_chars will cause linkage errors for 64-bit types.
94 (This is a bug in clang-7.)
95
96 If the clang requirement is bumped to >= clang-8, remove this ifdef block and its
97 associated check in cmake/compiler/clang/settings.cmake
98 */
99 template <>
100 struct For<uint64, void>
101 {
102 static Optional<uint64> FromString(std::string_view str, int base = 10)
103 {
104 if (str.empty())
105 return std::nullopt;
106 try
107 {
108 size_t n;
109 uint64 val = std::stoull(std::string(str), &n, base);
110 if (n != str.length())
111 return std::nullopt;
112 return val;
113 }
114 catch (...) { return std::nullopt; }
115 }
116
117 static std::string ToString(uint64 val)
118 {
119 return std::to_string(val);
120 }
121 };
122
123 template <>
124 struct For<int64, void>
125 {
126 static Optional<int64> FromString(std::string_view str, int base = 10)
127 {
128 try {
129 if (str.empty())
130 return std::nullopt;
131 size_t n;
132 int64 val = std::stoll(std::string(str), &n, base);
133 if (n != str.length())
134 return std::nullopt;
135 return val;
136 }
137 catch (...) { return std::nullopt; }
138 }
139
140 static std::string ToString(int64 val)
141 {
142 return std::to_string(val);
143 }
144 };
145#endif
146
147 template <>
148 struct For<bool, void>
149 {
150 static Optional<bool> FromString(std::string_view str, int strict = 0) /* this is int to match the signature for "proper" integral types */
151 {
152 if (strict)
153 {
154 if (str == "1")
155 return true;
156 if (str == "0")
157 return false;
158 return std::nullopt;
159 }
160 else
161 {
162 if ((str == "1") || StringEqualI(str, "y") || StringEqualI(str, "on") || StringEqualI(str, "yes") || StringEqualI(str, "true"))
163 return true;
164 if ((str == "0") || StringEqualI(str, "n") || StringEqualI(str, "off") || StringEqualI(str, "no") || StringEqualI(str, "false"))
165 return false;
166 return std::nullopt;
167 }
168 }
169
170 static std::string ToString(bool val)
171 {
172 return (val ? "1" : "0");
173 }
174 };
175
176#if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
177 template <typename T>
178 struct For<T, std::enable_if_t<std::is_floating_point_v<T>>>
179 {
180 static Optional<T> FromString(std::string_view str, std::chars_format fmt = std::chars_format())
181 {
182 if (str.empty())
183 return std::nullopt;
184
185 if (fmt == std::chars_format())
186 {
187 if (StringEqualI(str.substr(0, 2), "0x"))
188 {
189 fmt = std::chars_format::hex;
190 str.remove_prefix(2);
191 }
192 else
193 fmt = std::chars_format::general;
194
195 if (str.empty())
196 return std::nullopt;
197 }
198
199 char const* const start = str.data();
200 char const* const end = (start + str.length());
201
202 T val;
203 std::from_chars_result const res = std::from_chars(start, end, val, fmt);
204 if ((res.ptr == end) && (res.ec == std::errc()))
205 return val;
206 else
207 return std::nullopt;
208 }
209
210 // this allows generic converters for all numeric types (easier templating!)
211 static Optional<T> FromString(std::string_view str, int base)
212 {
213 if (base == 16)
214 return FromString(str, std::chars_format::hex);
215 else if (base == 10)
216 return FromString(str, std::chars_format::general);
217 else
218 return FromString(str, std::chars_format());
219 }
220
221 static std::string ToString(T val)
222 {
223 return std::to_string(val);
224 }
225 };
226#else
227 // @todo replace this once libc++ supports double args to from_chars
228 template <typename T>
229 struct For<T, std::enable_if_t<std::is_floating_point_v<T>>>
230 {
231 static Optional<T> FromString(std::string_view str, int base = 0)
232 {
233 try {
234 if (str.empty())
235 return std::nullopt;
236
237 if ((base == 10) && StringEqualI(str.substr(0, 2), "0x"))
238 return std::nullopt;
239
240 std::string tmp;
241 if (base == 16)
242 tmp.append("0x");
243 tmp.append(str);
244
245 size_t n;
246 T val = static_cast<T>(std::stold(tmp, &n));
247 if (n != tmp.length())
248 return std::nullopt;
249 return val;
250 }
251 catch (...) { return std::nullopt; }
252 }
253
254 static std::string ToString(T val)
255 {
256 return std::to_string(val);
257 }
258 };
259#endif
260}
261
262namespace Trinity
263{
264 template <typename Result, typename... Params>
265 Optional<Result> StringTo(std::string_view str, Params&&... params)
266 {
267 return Trinity::Impl::StringConvertImpl::For<Result>::FromString(str, std::forward<Params>(params)...);
268 }
269
270 template <typename Type, typename... Params>
271 std::string ToString(Type&& val, Params&&... params)
272 {
273 return Trinity::Impl::StringConvertImpl::For<std::decay_t<Type>>::ToString(std::forward<Type>(val), std::forward<Params>(params)...);
274 }
275}
276
277#endif
int64_t int64
Definition: Define.h:137
uint64_t uint64
Definition: Define.h:141
std::unordered_set< uint32 > params[2]
Definition: DisableMgr.cpp:50
#define ASSERT
Definition: Errors.h:68
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
bool StringEqualI(std::string_view a, std::string_view b)
Definition: Util.cpp:891
std::string ToString(Type &&val, Params &&... params)
Optional< Result > StringTo(std::string_view str, Params &&... params)
STL namespace.
static Optional< T > FromString(std::string_view str, std::chars_format fmt=std::chars_format())
static Optional< bool > FromString(std::string_view str, int strict=0)