unformat.cxx 1.2019.3 → 1.2019.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -2
- package/package.json +22 -7
- package/unformat.h +341 -341
- package/CMakeLists.txt +0 -39
package/README.md
CHANGED
|
@@ -1,21 +1,55 @@
|
|
|
1
1
|
**[Source available on GitHub](https://github.com/adamyaxley/Unformat)**
|
|
2
2
|
|
|
3
3
|
# Unformat
|
|
4
|
+
|
|
4
5
|
Parsing and extraction of original data from brace style "{}" formatted strings. It basically _unformats_ what you thought was formatted for good. It is developed and maintained by [Adam Yaxley](https://github.com/adamyaxley).
|
|
5
6
|
|
|
7
|
+
<br>
|
|
8
|
+
|
|
6
9
|
## Installation
|
|
7
10
|
|
|
8
11
|
Run:
|
|
9
|
-
|
|
12
|
+
|
|
13
|
+
```sh
|
|
10
14
|
$ npm i unformat.cxx
|
|
11
15
|
```
|
|
12
16
|
|
|
13
17
|
And then include `unformat.h` as follows:
|
|
18
|
+
|
|
14
19
|
```cxx
|
|
20
|
+
// main.cxx
|
|
15
21
|
#include "node_modules/unformat.cxx/unformat.h"
|
|
22
|
+
|
|
23
|
+
int main() { /* ... */ }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
And then compile with `clang++` or `g++` as usual.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
$ clang++ main.cxx # or, use g++
|
|
30
|
+
$ g++ main.cxx
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
You may also use a simpler approach:
|
|
34
|
+
|
|
35
|
+
```cxx
|
|
36
|
+
// main.cxx
|
|
37
|
+
#include <unformat.h>
|
|
38
|
+
|
|
39
|
+
int main() { /* ... */ }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If you add the path `node_modules/unformat.cxx` to your compiler's include paths.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ clang++ -I./node_modules/unformat.cxx main.cxx # or, use g++
|
|
46
|
+
$ g++ -I./node_modules/unformat.cxx main.cxx
|
|
16
47
|
```
|
|
17
48
|
|
|
49
|
+
<br>
|
|
50
|
+
|
|
18
51
|
## Quick Example
|
|
52
|
+
|
|
19
53
|
Unformat is simple to use and works on all basic types. See the below example for extracting a `std::string` and an 'int'
|
|
20
54
|
```c++
|
|
21
55
|
std::string name;
|
|
@@ -34,18 +68,31 @@ unformat("Harry is 18 years old.", format, name, age);
|
|
|
34
68
|
// name == "Harry" and age == 18
|
|
35
69
|
```
|
|
36
70
|
|
|
71
|
+
<br>
|
|
72
|
+
|
|
37
73
|
## How do I use this library?
|
|
74
|
+
|
|
38
75
|
Unformat is a single-file header only library so integration is easy. All you need to do is copy `unformat.h` into your project, and away you go.
|
|
39
76
|
|
|
77
|
+
<br>
|
|
78
|
+
|
|
40
79
|
## Public Domain
|
|
80
|
+
|
|
41
81
|
This software is completely open source and in the public domain. See LICENSE for details.
|
|
42
82
|
|
|
83
|
+
<br>
|
|
84
|
+
|
|
43
85
|
## Contributing
|
|
86
|
+
|
|
44
87
|
Pull requests are very welcome. You may also create Issues and I will have a look into it as soon as I can.
|
|
45
88
|
|
|
89
|
+
<br>
|
|
90
|
+
|
|
46
91
|
## Speed
|
|
92
|
+
|
|
47
93
|
Unformat is super awesome back to the future style lightning fast compared to traditional parsing methods. Below is the output from Google Benchmark on unformat_benchmark.cpp. Great Scott!
|
|
48
|
-
|
|
94
|
+
|
|
95
|
+
```bash
|
|
49
96
|
Run on (16 X 2993 MHz CPU s)
|
|
50
97
|
03/13/19 18:10:57
|
|
51
98
|
--------------------------------------------------------------------
|
|
@@ -63,5 +110,6 @@ StdScanf 1716 ns 1726 ns 407273
|
|
|
63
110
|
<br>
|
|
64
111
|
|
|
65
112
|
|
|
113
|
+
[](https://github.com/adamyaxley/Unformat)
|
|
66
114
|
[](https://nodef.github.io)
|
|
67
115
|

|
package/package.json
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unformat.cxx",
|
|
3
|
-
"version": "1.2019.
|
|
3
|
+
"version": "1.2019.4",
|
|
4
4
|
"description": "Fastest type-safe parsing library in the world for C++14 or C++17 (up to 300x faster than std::regex); Adam Yaxley (2017).",
|
|
5
|
-
"keywords": [
|
|
6
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cxx",
|
|
7
|
+
"library",
|
|
8
|
+
"parsing",
|
|
9
|
+
"formatting",
|
|
10
|
+
"unformat",
|
|
11
|
+
"unformatted",
|
|
12
|
+
"fast",
|
|
13
|
+
"safe",
|
|
14
|
+
"type-safe"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/nodef/unformat.cxx#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/nodef/unformat.cxx/issues"
|
|
19
|
+
},
|
|
7
20
|
"repository": {
|
|
8
21
|
"type": "git",
|
|
9
22
|
"url": "git+https://github.com/nodef/unformat.cxx.git"
|
|
10
23
|
},
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
24
|
+
"license": "UNLICENSED",
|
|
25
|
+
"author": "wolfram77@gmail.com",
|
|
26
|
+
"main": "unformat.h",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "bash build.sh test"
|
|
29
|
+
}
|
|
15
30
|
}
|
package/unformat.h
CHANGED
|
@@ -1,341 +1,341 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#ifdef _MSC_VER
|
|
4
|
-
#if _MSC_VER >= 1910 && _HAS_CXX17
|
|
5
|
-
#define UNFORMAT_CPP17
|
|
6
|
-
#endif
|
|
7
|
-
#endif
|
|
8
|
-
|
|
9
|
-
#include <string>
|
|
10
|
-
#ifdef UNFORMAT_CPP17
|
|
11
|
-
#include <string_view>
|
|
12
|
-
#endif
|
|
13
|
-
|
|
14
|
-
namespace
|
|
15
|
-
{
|
|
16
|
-
template <typename T>
|
|
17
|
-
void unformat_signed_int(const char* input, const char* inputEnd, T& output) noexcept
|
|
18
|
-
{
|
|
19
|
-
output = 0;
|
|
20
|
-
int sign = 1;
|
|
21
|
-
if (*input == '-')
|
|
22
|
-
{
|
|
23
|
-
sign = -1;
|
|
24
|
-
++input;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
#ifndef UNFORMAT_DISABLE_PLUS_SIGN
|
|
28
|
-
// Ignore plus
|
|
29
|
-
if (*input == '+')
|
|
30
|
-
{
|
|
31
|
-
++input;
|
|
32
|
-
}
|
|
33
|
-
#endif
|
|
34
|
-
|
|
35
|
-
while (input != inputEnd)
|
|
36
|
-
{
|
|
37
|
-
output *= 10;
|
|
38
|
-
output += (*input - '0');
|
|
39
|
-
++input;
|
|
40
|
-
}
|
|
41
|
-
output *= sign;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
template <typename T>
|
|
45
|
-
void unformat_unsigned_int(const char* input, const char* inputEnd, T& output) noexcept
|
|
46
|
-
{
|
|
47
|
-
output = 0;
|
|
48
|
-
while (input != inputEnd)
|
|
49
|
-
{
|
|
50
|
-
output *= 10;
|
|
51
|
-
output += (*input - '0');
|
|
52
|
-
++input;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
template <typename T>
|
|
57
|
-
void unformat_real(const char* input, const char* inputEnd, T& output) noexcept
|
|
58
|
-
{
|
|
59
|
-
long double f = 0;
|
|
60
|
-
|
|
61
|
-
// Check for negative
|
|
62
|
-
if (*input == '-')
|
|
63
|
-
{
|
|
64
|
-
f = -f;
|
|
65
|
-
++input;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
#ifndef UNFORMAT_DISABLE_PLUS_SIGN
|
|
69
|
-
// Ignore plus
|
|
70
|
-
if (*input == '+')
|
|
71
|
-
{
|
|
72
|
-
++input;
|
|
73
|
-
}
|
|
74
|
-
#endif
|
|
75
|
-
|
|
76
|
-
// Parse units
|
|
77
|
-
while (*input != '.' && input != inputEnd && *input != 'e')
|
|
78
|
-
{
|
|
79
|
-
f *= 10;
|
|
80
|
-
f += (*input - '0');
|
|
81
|
-
++input;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Parse decimal
|
|
85
|
-
if (*input == '.')
|
|
86
|
-
{
|
|
87
|
-
++input;
|
|
88
|
-
long double decimal = 1.0L;
|
|
89
|
-
while (input != inputEnd && *input != 'e')
|
|
90
|
-
{
|
|
91
|
-
decimal *= 0.1L;
|
|
92
|
-
f += (*input - '0') * decimal;
|
|
93
|
-
++input;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Parse exponent
|
|
98
|
-
if (*input == 'e')
|
|
99
|
-
{
|
|
100
|
-
++input;
|
|
101
|
-
if (*input == '+')
|
|
102
|
-
{
|
|
103
|
-
++input; // Skip + as unformat_signed_int can't handle it
|
|
104
|
-
}
|
|
105
|
-
int e;
|
|
106
|
-
unformat_signed_int(input, inputEnd, e);
|
|
107
|
-
f *= pow(10L, e);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
output = static_cast<T>(f);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
namespace ay
|
|
115
|
-
{
|
|
116
|
-
// Not defined on purpose for the general case. Note that you can define custom unformatters by defining new specialisations
|
|
117
|
-
// for this function.
|
|
118
|
-
template <typename T>
|
|
119
|
-
void unformat_arg(const char* input, const char* inputEnd, T& output) noexcept;
|
|
120
|
-
|
|
121
|
-
template <typename T>
|
|
122
|
-
T unformat_arg(const char* input, const char* inputEnd) noexcept
|
|
123
|
-
{
|
|
124
|
-
T output;
|
|
125
|
-
unformat_arg(input, inputEnd, output);
|
|
126
|
-
return output;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
#ifdef UNFORMAT_CPP17
|
|
130
|
-
template <typename T>
|
|
131
|
-
T unformat_arg(std::string_view input) noexcept
|
|
132
|
-
{
|
|
133
|
-
return unformat_arg<T>(input.data(), input.data() + input.length());
|
|
134
|
-
}
|
|
135
|
-
#endif
|
|
136
|
-
|
|
137
|
-
template <typename T>
|
|
138
|
-
T unformat_arg(std::string input) noexcept
|
|
139
|
-
{
|
|
140
|
-
return unformat_arg<T>(input.c_str(), input.c_str() + input.length());
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
template <>
|
|
144
|
-
inline void unformat_arg<char>(const char* input, const char* inputEnd, char& output) noexcept
|
|
145
|
-
{
|
|
146
|
-
output = input[0];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
template <>
|
|
150
|
-
inline void unformat_arg<unsigned char>(const char* input, const char* inputEnd, unsigned char& output) noexcept
|
|
151
|
-
{
|
|
152
|
-
output = input[0];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
#ifdef UNFORMAT_CPP17
|
|
156
|
-
template <>
|
|
157
|
-
inline void unformat_arg<std::string_view>(const char* input, const char* inputEnd, std::string_view& output) noexcept
|
|
158
|
-
{
|
|
159
|
-
output = std::string_view(input, inputEnd - input);
|
|
160
|
-
}
|
|
161
|
-
#endif
|
|
162
|
-
|
|
163
|
-
template <>
|
|
164
|
-
inline void unformat_arg<std::string>(const char* input, const char* inputEnd, std::string& output) noexcept
|
|
165
|
-
{
|
|
166
|
-
output.assign(input, inputEnd - input);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
template <>
|
|
170
|
-
inline void unformat_arg<float>(const char* input, const char* inputEnd, float& output) noexcept
|
|
171
|
-
{
|
|
172
|
-
unformat_real<float>(input, inputEnd, output);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
template <>
|
|
176
|
-
inline void unformat_arg<double>(const char* input, const char* inputEnd, double& output) noexcept
|
|
177
|
-
{
|
|
178
|
-
unformat_real<double>(input, inputEnd, output);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
template <>
|
|
182
|
-
inline void unformat_arg<short>(const char* input, const char* inputEnd, short& output) noexcept
|
|
183
|
-
{
|
|
184
|
-
unformat_signed_int<short>(input, inputEnd, output);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
template <>
|
|
188
|
-
inline void unformat_arg<int>(const char* input, const char* inputEnd, int& output) noexcept
|
|
189
|
-
{
|
|
190
|
-
unformat_signed_int<int>(input, inputEnd, output);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
template <>
|
|
194
|
-
inline void unformat_arg<long>(const char* input, const char* inputEnd, long& output) noexcept
|
|
195
|
-
{
|
|
196
|
-
unformat_signed_int<long>(input, inputEnd, output);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
template <>
|
|
200
|
-
inline void unformat_arg<long long>(const char* input, const char* inputEnd, long long& output) noexcept
|
|
201
|
-
{
|
|
202
|
-
unformat_signed_int<long long>(input, inputEnd, output);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
template <>
|
|
206
|
-
inline void unformat_arg<unsigned short>(const char* input, const char* inputEnd, unsigned short& output) noexcept
|
|
207
|
-
{
|
|
208
|
-
unformat_unsigned_int<unsigned short>(input, inputEnd, output);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
template <>
|
|
212
|
-
inline void unformat_arg<unsigned int>(const char* input, const char* inputEnd, unsigned int& output) noexcept
|
|
213
|
-
{
|
|
214
|
-
unformat_unsigned_int<unsigned int>(input, inputEnd, output);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
template <>
|
|
218
|
-
inline void unformat_arg<unsigned long>(const char* input, const char* inputEnd, unsigned long& output) noexcept
|
|
219
|
-
{
|
|
220
|
-
unformat_unsigned_int<unsigned long>(input, inputEnd, output);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
template <>
|
|
224
|
-
inline void unformat_arg<unsigned long long>(const char* input, const char* inputEnd, unsigned long long& output) noexcept
|
|
225
|
-
{
|
|
226
|
-
unformat_unsigned_int<unsigned long long>(input, inputEnd, output);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
struct format;
|
|
230
|
-
|
|
231
|
-
constexpr format make_format_non_template(const char* str, std::size_t N);
|
|
232
|
-
|
|
233
|
-
struct format
|
|
234
|
-
{
|
|
235
|
-
constexpr format() {}
|
|
236
|
-
|
|
237
|
-
template <std::size_t N>
|
|
238
|
-
constexpr format(const char(&str)[N])
|
|
239
|
-
{
|
|
240
|
-
*this = make_format_non_template(str, N);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
format(const char* str)
|
|
244
|
-
{
|
|
245
|
-
*this = make_format_non_template(str, std::strlen(str));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
static constexpr std::size_t MAX_COUNT{ 16 };
|
|
249
|
-
std::size_t offsets[MAX_COUNT]{};
|
|
250
|
-
char endChar[MAX_COUNT]{};
|
|
251
|
-
std::size_t count{ 0 };
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
template <std::size_t N>
|
|
255
|
-
constexpr format make_format(const char(&str)[N]) noexcept
|
|
256
|
-
{
|
|
257
|
-
return make_format_non_template(str, N);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
constexpr format make_format_non_template(const char* str, std::size_t N)
|
|
261
|
-
{
|
|
262
|
-
format format;
|
|
263
|
-
|
|
264
|
-
std::size_t from = 0;
|
|
265
|
-
for (std::size_t to = 0; to < N - 1; to++)
|
|
266
|
-
{
|
|
267
|
-
if (str[to] == '{' && str[to + 1] == '}')
|
|
268
|
-
{
|
|
269
|
-
if (format.count >= format::MAX_COUNT)
|
|
270
|
-
{
|
|
271
|
-
// Stops compilation in a constexpr context
|
|
272
|
-
throw std::exception("Max number of {} has been exceeded.");
|
|
273
|
-
}
|
|
274
|
-
format.offsets[format.count] = to - from;
|
|
275
|
-
format.endChar[format.count] = str[to + 2]; // May be '\0' which is fine
|
|
276
|
-
++format.count;
|
|
277
|
-
// Advance markers
|
|
278
|
-
from = to + 2;
|
|
279
|
-
to = from;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return format;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Empty function to end recursion, no more args to process
|
|
287
|
-
inline void unformat_internal(std::size_t inputPos, const char* input, const format& format) noexcept
|
|
288
|
-
{
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
template <typename T, typename... TRest>
|
|
292
|
-
void unformat_internal(std::size_t inputPos, const char* input, const format& format, T& first, TRest&... rest) noexcept
|
|
293
|
-
{
|
|
294
|
-
const std::size_t argNo = format.count - sizeof...(rest) - 1;
|
|
295
|
-
|
|
296
|
-
// Get the location of the first brace
|
|
297
|
-
const auto offset = format.offsets[argNo];
|
|
298
|
-
|
|
299
|
-
// Find input string
|
|
300
|
-
inputPos += offset;
|
|
301
|
-
auto inputEnd = inputPos;
|
|
302
|
-
while (input[inputEnd] != format.endChar[argNo])
|
|
303
|
-
{
|
|
304
|
-
++inputEnd;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Process this arg
|
|
308
|
-
unformat_arg(&input[inputPos], &input[inputEnd], first);
|
|
309
|
-
|
|
310
|
-
// Process TRest
|
|
311
|
-
unformat_internal(inputEnd, input, format, rest...);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Parses and extracts data from 'input' given a braced styled "{}" 'format' into 'args...'
|
|
315
|
-
// For example:
|
|
316
|
-
// std::string name, int age;
|
|
317
|
-
// constexpr auto format = make_format("{} is {} years old.");
|
|
318
|
-
// unformat("Harry is 18 years old.", format, name, age);
|
|
319
|
-
//
|
|
320
|
-
// Then the following data is extracted:
|
|
321
|
-
// name == "Harry" and age == 18
|
|
322
|
-
template <typename... Args>
|
|
323
|
-
void unformat(const char* input, const format& format, Args&... args) noexcept
|
|
324
|
-
{
|
|
325
|
-
unformat_internal(0, input, format, args...);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Parses and extracts data from 'input' given a braced styled "{}" 'format' into 'args...'
|
|
329
|
-
// For example:
|
|
330
|
-
// std::string name, int age;
|
|
331
|
-
// constexpr auto format = make_format("{} is {} years old.");
|
|
332
|
-
// unformat("Harry is 18 years old.", format, name, age);
|
|
333
|
-
//
|
|
334
|
-
// Then the following data is extracted:
|
|
335
|
-
// name == "Harry" and age == 18
|
|
336
|
-
template <typename... Args>
|
|
337
|
-
void unformat(const std::string& input, const format& format, Args&... args) noexcept
|
|
338
|
-
{
|
|
339
|
-
unformat_internal(0, input.c_str(), format, args...);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef _MSC_VER
|
|
4
|
+
#if _MSC_VER >= 1910 && _HAS_CXX17
|
|
5
|
+
#define UNFORMAT_CPP17
|
|
6
|
+
#endif
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
#include <string>
|
|
10
|
+
#ifdef UNFORMAT_CPP17
|
|
11
|
+
#include <string_view>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
namespace
|
|
15
|
+
{
|
|
16
|
+
template <typename T>
|
|
17
|
+
void unformat_signed_int(const char* input, const char* inputEnd, T& output) noexcept
|
|
18
|
+
{
|
|
19
|
+
output = 0;
|
|
20
|
+
int sign = 1;
|
|
21
|
+
if (*input == '-')
|
|
22
|
+
{
|
|
23
|
+
sign = -1;
|
|
24
|
+
++input;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#ifndef UNFORMAT_DISABLE_PLUS_SIGN
|
|
28
|
+
// Ignore plus
|
|
29
|
+
if (*input == '+')
|
|
30
|
+
{
|
|
31
|
+
++input;
|
|
32
|
+
}
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
while (input != inputEnd)
|
|
36
|
+
{
|
|
37
|
+
output *= 10;
|
|
38
|
+
output += (*input - '0');
|
|
39
|
+
++input;
|
|
40
|
+
}
|
|
41
|
+
output *= sign;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
template <typename T>
|
|
45
|
+
void unformat_unsigned_int(const char* input, const char* inputEnd, T& output) noexcept
|
|
46
|
+
{
|
|
47
|
+
output = 0;
|
|
48
|
+
while (input != inputEnd)
|
|
49
|
+
{
|
|
50
|
+
output *= 10;
|
|
51
|
+
output += (*input - '0');
|
|
52
|
+
++input;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
template <typename T>
|
|
57
|
+
void unformat_real(const char* input, const char* inputEnd, T& output) noexcept
|
|
58
|
+
{
|
|
59
|
+
long double f = 0;
|
|
60
|
+
|
|
61
|
+
// Check for negative
|
|
62
|
+
if (*input == '-')
|
|
63
|
+
{
|
|
64
|
+
f = -f;
|
|
65
|
+
++input;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#ifndef UNFORMAT_DISABLE_PLUS_SIGN
|
|
69
|
+
// Ignore plus
|
|
70
|
+
if (*input == '+')
|
|
71
|
+
{
|
|
72
|
+
++input;
|
|
73
|
+
}
|
|
74
|
+
#endif
|
|
75
|
+
|
|
76
|
+
// Parse units
|
|
77
|
+
while (*input != '.' && input != inputEnd && *input != 'e')
|
|
78
|
+
{
|
|
79
|
+
f *= 10;
|
|
80
|
+
f += (*input - '0');
|
|
81
|
+
++input;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Parse decimal
|
|
85
|
+
if (*input == '.')
|
|
86
|
+
{
|
|
87
|
+
++input;
|
|
88
|
+
long double decimal = 1.0L;
|
|
89
|
+
while (input != inputEnd && *input != 'e')
|
|
90
|
+
{
|
|
91
|
+
decimal *= 0.1L;
|
|
92
|
+
f += (*input - '0') * decimal;
|
|
93
|
+
++input;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Parse exponent
|
|
98
|
+
if (*input == 'e')
|
|
99
|
+
{
|
|
100
|
+
++input;
|
|
101
|
+
if (*input == '+')
|
|
102
|
+
{
|
|
103
|
+
++input; // Skip + as unformat_signed_int can't handle it
|
|
104
|
+
}
|
|
105
|
+
int e;
|
|
106
|
+
unformat_signed_int(input, inputEnd, e);
|
|
107
|
+
f *= pow(10L, e);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
output = static_cast<T>(f);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
namespace ay
|
|
115
|
+
{
|
|
116
|
+
// Not defined on purpose for the general case. Note that you can define custom unformatters by defining new specialisations
|
|
117
|
+
// for this function.
|
|
118
|
+
template <typename T>
|
|
119
|
+
void unformat_arg(const char* input, const char* inputEnd, T& output) noexcept;
|
|
120
|
+
|
|
121
|
+
template <typename T>
|
|
122
|
+
T unformat_arg(const char* input, const char* inputEnd) noexcept
|
|
123
|
+
{
|
|
124
|
+
T output;
|
|
125
|
+
unformat_arg(input, inputEnd, output);
|
|
126
|
+
return output;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#ifdef UNFORMAT_CPP17
|
|
130
|
+
template <typename T>
|
|
131
|
+
T unformat_arg(std::string_view input) noexcept
|
|
132
|
+
{
|
|
133
|
+
return unformat_arg<T>(input.data(), input.data() + input.length());
|
|
134
|
+
}
|
|
135
|
+
#endif
|
|
136
|
+
|
|
137
|
+
template <typename T>
|
|
138
|
+
T unformat_arg(std::string input) noexcept
|
|
139
|
+
{
|
|
140
|
+
return unformat_arg<T>(input.c_str(), input.c_str() + input.length());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
template <>
|
|
144
|
+
inline void unformat_arg<char>(const char* input, const char* inputEnd, char& output) noexcept
|
|
145
|
+
{
|
|
146
|
+
output = input[0];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
template <>
|
|
150
|
+
inline void unformat_arg<unsigned char>(const char* input, const char* inputEnd, unsigned char& output) noexcept
|
|
151
|
+
{
|
|
152
|
+
output = input[0];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#ifdef UNFORMAT_CPP17
|
|
156
|
+
template <>
|
|
157
|
+
inline void unformat_arg<std::string_view>(const char* input, const char* inputEnd, std::string_view& output) noexcept
|
|
158
|
+
{
|
|
159
|
+
output = std::string_view(input, inputEnd - input);
|
|
160
|
+
}
|
|
161
|
+
#endif
|
|
162
|
+
|
|
163
|
+
template <>
|
|
164
|
+
inline void unformat_arg<std::string>(const char* input, const char* inputEnd, std::string& output) noexcept
|
|
165
|
+
{
|
|
166
|
+
output.assign(input, inputEnd - input);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
template <>
|
|
170
|
+
inline void unformat_arg<float>(const char* input, const char* inputEnd, float& output) noexcept
|
|
171
|
+
{
|
|
172
|
+
unformat_real<float>(input, inputEnd, output);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
template <>
|
|
176
|
+
inline void unformat_arg<double>(const char* input, const char* inputEnd, double& output) noexcept
|
|
177
|
+
{
|
|
178
|
+
unformat_real<double>(input, inputEnd, output);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
template <>
|
|
182
|
+
inline void unformat_arg<short>(const char* input, const char* inputEnd, short& output) noexcept
|
|
183
|
+
{
|
|
184
|
+
unformat_signed_int<short>(input, inputEnd, output);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
template <>
|
|
188
|
+
inline void unformat_arg<int>(const char* input, const char* inputEnd, int& output) noexcept
|
|
189
|
+
{
|
|
190
|
+
unformat_signed_int<int>(input, inputEnd, output);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
template <>
|
|
194
|
+
inline void unformat_arg<long>(const char* input, const char* inputEnd, long& output) noexcept
|
|
195
|
+
{
|
|
196
|
+
unformat_signed_int<long>(input, inputEnd, output);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
template <>
|
|
200
|
+
inline void unformat_arg<long long>(const char* input, const char* inputEnd, long long& output) noexcept
|
|
201
|
+
{
|
|
202
|
+
unformat_signed_int<long long>(input, inputEnd, output);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
template <>
|
|
206
|
+
inline void unformat_arg<unsigned short>(const char* input, const char* inputEnd, unsigned short& output) noexcept
|
|
207
|
+
{
|
|
208
|
+
unformat_unsigned_int<unsigned short>(input, inputEnd, output);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
template <>
|
|
212
|
+
inline void unformat_arg<unsigned int>(const char* input, const char* inputEnd, unsigned int& output) noexcept
|
|
213
|
+
{
|
|
214
|
+
unformat_unsigned_int<unsigned int>(input, inputEnd, output);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
template <>
|
|
218
|
+
inline void unformat_arg<unsigned long>(const char* input, const char* inputEnd, unsigned long& output) noexcept
|
|
219
|
+
{
|
|
220
|
+
unformat_unsigned_int<unsigned long>(input, inputEnd, output);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
template <>
|
|
224
|
+
inline void unformat_arg<unsigned long long>(const char* input, const char* inputEnd, unsigned long long& output) noexcept
|
|
225
|
+
{
|
|
226
|
+
unformat_unsigned_int<unsigned long long>(input, inputEnd, output);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
struct format;
|
|
230
|
+
|
|
231
|
+
constexpr format make_format_non_template(const char* str, std::size_t N);
|
|
232
|
+
|
|
233
|
+
struct format
|
|
234
|
+
{
|
|
235
|
+
constexpr format() {}
|
|
236
|
+
|
|
237
|
+
template <std::size_t N>
|
|
238
|
+
constexpr format(const char(&str)[N])
|
|
239
|
+
{
|
|
240
|
+
*this = make_format_non_template(str, N);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
format(const char* str)
|
|
244
|
+
{
|
|
245
|
+
*this = make_format_non_template(str, std::strlen(str));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
static constexpr std::size_t MAX_COUNT{ 16 };
|
|
249
|
+
std::size_t offsets[MAX_COUNT]{};
|
|
250
|
+
char endChar[MAX_COUNT]{};
|
|
251
|
+
std::size_t count{ 0 };
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
template <std::size_t N>
|
|
255
|
+
constexpr format make_format(const char(&str)[N]) noexcept
|
|
256
|
+
{
|
|
257
|
+
return make_format_non_template(str, N);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
constexpr format make_format_non_template(const char* str, std::size_t N)
|
|
261
|
+
{
|
|
262
|
+
format format;
|
|
263
|
+
|
|
264
|
+
std::size_t from = 0;
|
|
265
|
+
for (std::size_t to = 0; to < N - 1; to++)
|
|
266
|
+
{
|
|
267
|
+
if (str[to] == '{' && str[to + 1] == '}')
|
|
268
|
+
{
|
|
269
|
+
if (format.count >= format::MAX_COUNT)
|
|
270
|
+
{
|
|
271
|
+
// Stops compilation in a constexpr context
|
|
272
|
+
throw std::exception("Max number of {} has been exceeded.");
|
|
273
|
+
}
|
|
274
|
+
format.offsets[format.count] = to - from;
|
|
275
|
+
format.endChar[format.count] = str[to + 2]; // May be '\0' which is fine
|
|
276
|
+
++format.count;
|
|
277
|
+
// Advance markers
|
|
278
|
+
from = to + 2;
|
|
279
|
+
to = from;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return format;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Empty function to end recursion, no more args to process
|
|
287
|
+
inline void unformat_internal(std::size_t inputPos, const char* input, const format& format) noexcept
|
|
288
|
+
{
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
template <typename T, typename... TRest>
|
|
292
|
+
void unformat_internal(std::size_t inputPos, const char* input, const format& format, T& first, TRest&... rest) noexcept
|
|
293
|
+
{
|
|
294
|
+
const std::size_t argNo = format.count - sizeof...(rest) - 1;
|
|
295
|
+
|
|
296
|
+
// Get the location of the first brace
|
|
297
|
+
const auto offset = format.offsets[argNo];
|
|
298
|
+
|
|
299
|
+
// Find input string
|
|
300
|
+
inputPos += offset;
|
|
301
|
+
auto inputEnd = inputPos;
|
|
302
|
+
while (input[inputEnd] != format.endChar[argNo])
|
|
303
|
+
{
|
|
304
|
+
++inputEnd;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Process this arg
|
|
308
|
+
unformat_arg(&input[inputPos], &input[inputEnd], first);
|
|
309
|
+
|
|
310
|
+
// Process TRest
|
|
311
|
+
unformat_internal(inputEnd, input, format, rest...);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Parses and extracts data from 'input' given a braced styled "{}" 'format' into 'args...'
|
|
315
|
+
// For example:
|
|
316
|
+
// std::string name, int age;
|
|
317
|
+
// constexpr auto format = make_format("{} is {} years old.");
|
|
318
|
+
// unformat("Harry is 18 years old.", format, name, age);
|
|
319
|
+
//
|
|
320
|
+
// Then the following data is extracted:
|
|
321
|
+
// name == "Harry" and age == 18
|
|
322
|
+
template <typename... Args>
|
|
323
|
+
void unformat(const char* input, const format& format, Args&... args) noexcept
|
|
324
|
+
{
|
|
325
|
+
unformat_internal(0, input, format, args...);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Parses and extracts data from 'input' given a braced styled "{}" 'format' into 'args...'
|
|
329
|
+
// For example:
|
|
330
|
+
// std::string name, int age;
|
|
331
|
+
// constexpr auto format = make_format("{} is {} years old.");
|
|
332
|
+
// unformat("Harry is 18 years old.", format, name, age);
|
|
333
|
+
//
|
|
334
|
+
// Then the following data is extracted:
|
|
335
|
+
// name == "Harry" and age == 18
|
|
336
|
+
template <typename... Args>
|
|
337
|
+
void unformat(const std::string& input, const format& format, Args&... args) noexcept
|
|
338
|
+
{
|
|
339
|
+
unformat_internal(0, input.c_str(), format, args...);
|
|
340
|
+
}
|
|
341
|
+
}
|
package/CMakeLists.txt
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.0)
|
|
2
|
-
|
|
3
|
-
project(Unformat)
|
|
4
|
-
|
|
5
|
-
set(CMAKE_CXX_STANDARD 11)
|
|
6
|
-
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
7
|
-
|
|
8
|
-
include(DownloadProject/DownloadProject.cmake)
|
|
9
|
-
|
|
10
|
-
# Functional test
|
|
11
|
-
download_project(PROJ googletest
|
|
12
|
-
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
13
|
-
GIT_TAG release-1.8.0
|
|
14
|
-
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
|
15
|
-
)
|
|
16
|
-
# Prevent GoogleTest from overriding our compiler/linker options
|
|
17
|
-
# when building with Visual Studio
|
|
18
|
-
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
19
|
-
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
|
20
|
-
add_executable(unformat_test unformat.h unformat_test.cpp)
|
|
21
|
-
target_link_libraries(unformat_test gtest gmock_main)
|
|
22
|
-
|
|
23
|
-
# Benchmark
|
|
24
|
-
if (CMAKE_VERSION VERSION_LESS 3.2)
|
|
25
|
-
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
|
26
|
-
else()
|
|
27
|
-
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
|
28
|
-
endif()
|
|
29
|
-
|
|
30
|
-
download_project(PROJ benchmark
|
|
31
|
-
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
32
|
-
GIT_TAG v1.3.0
|
|
33
|
-
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
set(BENCHMARK_ENABLE_TESTING OFF)
|
|
37
|
-
add_subdirectory(${benchmark_SOURCE_DIR} ${benchmark_BINARY_DIR})
|
|
38
|
-
add_executable(unformat_benchmark unformat.h unformat_benchmark.cpp)
|
|
39
|
-
target_link_libraries(unformat_benchmark benchmark)
|