@@ -22,6 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
SOFTWARE.
23
23
*/
24
24
25
+ // Important note: the types in this file are only intended for compile-time construction
26
+ // but consteval doesn't exist in C++17
27
+
25
28
#ifndef CONSTEXPR_JSON_HPP_INCLUDED
26
29
#define CONSTEXPR_JSON_HPP_INCLUDED
27
30
@@ -30,7 +33,6 @@ SOFTWARE.
30
33
#include < cstdint>
31
34
#include < stdexcept>
32
35
#include < string_view>
33
- #include < variant>
34
36
35
37
namespace constexpr_json {
36
38
template <typename First, typename Second> struct pair
@@ -58,23 +60,138 @@ template<typename T> struct span
58
60
const T *end_;
59
61
};
60
62
61
- struct json ;
63
+ template <typename CharType> struct basic_json ;
64
+ template <typename CharType> using basic_array_t = span<basic_json<CharType>>;
65
+ template <typename CharType> using basic_value_pair_t = pair<std::basic_string_view<CharType>, basic_json<CharType>>;
66
+ template <typename CharType> using basic_object_t = span<basic_value_pair_t <CharType>>;
62
67
63
- using array_t = span<json>;
64
- using value_pair_t = pair<std::string_view, json>;
65
- using object_t = span<value_pair_t >;
66
68
using binary_t = span<std::uint8_t >;
67
69
68
- using data_t = std::
69
- variant<std::monostate, bool , binary_t , array_t , object_t , std::int64_t , std::uint64_t , double , std::string_view>;
70
+ template <typename CharType> struct data_variant
71
+ {
72
+ struct monostate
73
+ {
74
+ };
75
+
76
+ union value_t {
77
+ monostate empty_;
78
+ bool bool_;
79
+ binary_t binary_;
80
+ basic_array_t <CharType> array_;
81
+ basic_object_t <CharType> object_;
82
+ std::int64_t int64_t_;
83
+ std::uint64_t uint64_t_;
84
+ double double_;
85
+ std::basic_string_view<CharType> string_view_;
86
+
87
+ constexpr explicit value_t () : empty_{} {}
88
+ constexpr explicit value_t (monostate) : value_t () {}
89
+ constexpr explicit value_t (bool b) : bool_{ b } {}
90
+ constexpr explicit value_t (binary_t b) : binary_{ b } {}
91
+ constexpr explicit value_t (basic_array_t <CharType> a) : array_{ a } {}
92
+ constexpr explicit value_t (basic_object_t <CharType> o) : object_{ o } {}
93
+ constexpr explicit value_t (std::int64_t i) : int64_t_{ i } {}
94
+ constexpr explicit value_t (std::uint64_t i) : uint64_t_{ i } {}
95
+ constexpr explicit value_t (double d) : double_{ d } {}
96
+ constexpr explicit value_t (std::basic_string_view<CharType> s) : string_view_{ s } {}
97
+ };
98
+
99
+ enum struct selected_type { empty, boolean, binary, array, object, integer, uinteger, floating_point, string };
100
+
101
+ value_t value{ monostate{} };
102
+ selected_type selected{ selected_type::empty };
103
+
104
+ // letting these be implicit measurably improves construction performance
105
+
106
+ constexpr data_variant () = default;
107
+ // cppcheck-suppress noExplicitConstructor
108
+ constexpr data_variant (monostate) : data_variant() {}
109
+ // cppcheck-suppress noExplicitConstructor
110
+ constexpr data_variant (bool b) : value{ b }, selected{ selected_type::boolean } {}
111
+ // cppcheck-suppress noExplicitConstructor
112
+ constexpr data_variant (basic_array_t <CharType> a) : value{ a }, selected{ selected_type::array } {}
113
+ // cppcheck-suppress noExplicitConstructor
114
+ constexpr data_variant (basic_object_t <CharType> o) : value{ o }, selected{ selected_type::object } {}
115
+ // cppcheck-suppress noExplicitConstructor
116
+ constexpr data_variant (std::int64_t i) : value{ i }, selected{ selected_type::integer } {}
117
+ // cppcheck-suppress noExplicitConstructor
118
+ constexpr data_variant (std::uint64_t i) : value{ i }, selected{ selected_type::uinteger } {}
119
+ // cppcheck-suppress noExplicitConstructor
120
+ constexpr data_variant (double d) : value{ d }, selected{ selected_type::floating_point } {}
121
+ // cppcheck-suppress noExplicitConstructor
122
+ constexpr data_variant (std::basic_string_view<CharType> s) : value{ s }, selected{ selected_type::string } {}
123
+
124
+ [[nodiscard]] constexpr const bool *get_if_boolean () const noexcept
125
+ {
126
+ if (selected == selected_type::boolean) {
127
+ return &value.bool_ ;
128
+ } else {
129
+ return nullptr ;
130
+ }
131
+ }
132
+
133
+ [[nodiscard]] constexpr const basic_array_t <CharType> *get_if_array () const noexcept
134
+ {
135
+ if (selected == selected_type::array) {
136
+ return &value.array_ ;
137
+ } else {
138
+ return nullptr ;
139
+ }
140
+ }
141
+ [[nodiscard]] constexpr const basic_object_t <CharType> *get_if_object () const noexcept
142
+ {
143
+ if (selected == selected_type::object) {
144
+ return &value.object_ ;
145
+ } else {
146
+ return nullptr ;
147
+ }
148
+ }
149
+ [[nodiscard]] constexpr const std::int64_t *get_if_integer () const noexcept
150
+ {
151
+ if (selected == selected_type::integer) {
152
+ return &value.int64_t_ ;
153
+ } else {
154
+ return nullptr ;
155
+ }
156
+ }
157
+ [[nodiscard]] constexpr const std::uint64_t *get_if_uinteger () const noexcept
158
+ {
159
+ if (selected == selected_type::uinteger) {
160
+ return &value.uint64_t_ ;
161
+ } else {
162
+ return nullptr ;
163
+ }
164
+ }
165
+
166
+ [[nodiscard]] constexpr const double *get_if_floating_point () const noexcept
167
+ {
168
+ if (selected == selected_type::floating_point) {
169
+ return &value.double_ ;
170
+ } else {
171
+ return nullptr ;
172
+ }
173
+ }
174
+
175
+ [[nodiscard]] constexpr const std::basic_string_view<CharType> *get_if_string () const noexcept
176
+ {
177
+ if (selected == selected_type::string) {
178
+ return &value.string_view_ ;
179
+ } else {
180
+ return nullptr ;
181
+ }
182
+ }
183
+ };
70
184
71
- struct json
185
+ template < typename CharType> struct basic_json
72
186
{
187
+ using data_t = data_variant<CharType>;
188
+
73
189
struct iterator
74
190
{
75
- constexpr explicit iterator (const json &value, std::size_t index = 0 ) : parent_value_(&value), index_{ index } {}
191
+ constexpr explicit iterator (const basic_json &value, std::size_t index = 0 ) : parent_value_(&value), index_{ index }
192
+ {}
76
193
77
- constexpr const json &operator *() const
194
+ constexpr const basic_json &operator *() const
78
195
{
79
196
if (parent_value_->is_array ()) {
80
197
return (*parent_value_)[index_];
@@ -85,14 +202,14 @@ struct json
85
202
}
86
203
}
87
204
88
- constexpr const json *operator ->() const { return &(*(*this )); }
205
+ constexpr const basic_json *operator ->() const { return &(*(*this )); }
89
206
90
207
constexpr std::size_t index () const { return index_; }
91
208
92
- constexpr const json &value () const { return *(*this ); }
209
+ constexpr const basic_json &value () const { return *(*this ); }
93
210
94
211
95
- constexpr std::string_view key () const
212
+ constexpr std::basic_string_view<CharType> key () const
96
213
{
97
214
if (parent_value_->is_object ()) {
98
215
return std::next (parent_value_->object_data ().begin (), static_cast <std::ptrdiff_t >(index_))->first ;
@@ -153,7 +270,7 @@ struct json
153
270
return *this ;
154
271
}
155
272
156
- const json *parent_value_{ nullptr };
273
+ const basic_json *parent_value_{ nullptr };
157
274
std::size_t index_{ 0 };
158
275
};
159
276
@@ -170,14 +287,14 @@ struct json
170
287
constexpr std::size_t size () const noexcept
171
288
{
172
289
if (is_null ()) { return 0 ; }
173
- if (is_object ()) { return std::get_if< object_t >(& data)->size (); }
290
+ if (is_object ()) { return data. get_if_object ( )->size (); }
174
291
175
- if (is_array ()) { return std::get_if< array_t >(& data)->size (); }
292
+ if (is_array ()) { return data. get_if_array ( )->size (); }
176
293
177
294
return 1 ;
178
295
}
179
296
180
- constexpr const json &operator [](const std::size_t idx) const
297
+ constexpr const basic_json &operator [](const std::size_t idx) const
181
298
{
182
299
if (const auto &children = array_data (); idx < children.size ()) {
183
300
return *std::next (children.begin (), static_cast <std::ptrdiff_t >(idx));
@@ -195,7 +312,7 @@ struct json
195
312
return end ();
196
313
}
197
314
198
- constexpr const json &operator [](const std::string_view key) const
315
+ constexpr const basic_json &operator [](const std::string_view key) const
199
316
{
200
317
const auto &children = object_data ();
201
318
@@ -221,82 +338,80 @@ struct json
221
338
}
222
339
}
223
340
224
- constexpr const array_t &array_data () const
341
+ constexpr const auto &array_data () const
225
342
{
226
- if (const auto *result = std::get_if< array_t >(& data); result != nullptr ) {
343
+ if (const auto *result = data. get_if_array ( ); result != nullptr ) {
227
344
return *result;
228
345
} else {
229
346
throw std::runtime_error (" value is not an array type" );
230
347
}
231
348
}
232
349
233
- constexpr const object_t &object_data () const
350
+ constexpr const auto &object_data () const
234
351
{
235
- if (const auto *result = std::get_if< object_t >(& data); result != nullptr ) {
352
+ if (const auto *result = data. get_if_object ( ); result != nullptr ) {
236
353
return *result;
237
354
} else {
238
355
throw std::runtime_error (" value is not an object type" );
239
356
}
240
357
}
241
358
242
- constexpr static json object () { return json{ object_t {} }; }
243
-
244
- constexpr static json array () { return json{ array_t {} }; }
245
-
359
+ constexpr static basic_json object () { return basic_json{ data_t { basic_object_t <CharType>{} } }; }
360
+ constexpr static basic_json array () { return basic_json{ data_t { basic_array_t <CharType>{} } }; }
246
361
247
362
template <typename Type> constexpr Type get () const
248
363
{
249
364
if constexpr (std::is_same_v<Type, std::uint64_t > || std::is_same_v<Type, std::int64_t >) {
250
- if (const auto *uint_value = std::get_if<std:: uint64_t >(& data); uint_value != nullptr ) {
365
+ if (const auto *uint_value = data. get_if_uinteger ( ); uint_value != nullptr ) {
251
366
return Type (*uint_value);
252
- } else if (const auto *value = std::get_if<std:: int64_t >(& data); value != nullptr ) {
367
+ } else if (const auto *value = data. get_if_integer ( ); value != nullptr ) {
253
368
return Type (*value);
254
369
}
255
370
} else if constexpr (std::is_same_v<Type, double >) {
256
- if (const auto *value = std::get_if< double >(& data); value != nullptr ) { return *value; }
371
+ if (const auto *value = data. get_if_floating_point ( ); value != nullptr ) { return *value; }
257
372
} else if constexpr (std::is_same_v<Type, std::string_view>) {
258
- if (const auto *value = std::get_if<std::string_view>(& data); value != nullptr ) { return *value; }
373
+ if (const auto *value = data. get_if_string ( ); value != nullptr ) { return *value; }
259
374
} else if constexpr (std::is_same_v<Type, bool >) {
260
- if (const auto *value = std::get_if< bool >(& data); value != nullptr ) { return *value; }
375
+ if (const auto *value = data. get_if_boolean ( ); value != nullptr ) { return *value; }
261
376
} else {
262
377
throw std::runtime_error (" Unexpected type for get()" );
263
378
}
264
379
265
380
throw std::runtime_error (" Incorrect type for get()" );
266
381
}
267
382
268
- constexpr bool is_object () const noexcept { return std::holds_alternative< object_t >(data) ; }
383
+ constexpr bool is_object () const noexcept { return data. selected == data_t ::selected_type::object ; }
269
384
270
- constexpr bool is_array () const noexcept { return std::holds_alternative< array_t >(data) ; }
385
+ constexpr bool is_array () const noexcept { return data. selected == data_t ::selected_type::array ; }
271
386
272
- constexpr bool is_string () const noexcept { return std::holds_alternative<std::string_view>(data) ; }
387
+ constexpr bool is_string () const noexcept { return data. selected == data_t ::selected_type::string ; }
273
388
274
- constexpr bool is_boolean () const noexcept { return std::holds_alternative< bool >(data) ; }
389
+ constexpr bool is_boolean () const noexcept { return data. selected == data_t ::selected_type::boolean ; }
275
390
276
391
constexpr bool is_structured () const noexcept { return is_object () || is_array (); }
277
392
278
393
constexpr bool is_number () const noexcept { return is_number_integer () || is_number_float (); }
279
394
280
395
constexpr double as_number_float () const
281
396
{
282
- if (const double *value = std::get_if< double >(& data); value != nullptr ) {
397
+ if (const double *value = data. get_if_floating_point ( ); value != nullptr ) {
283
398
return *value;
284
399
} else {
285
400
throw std::runtime_error (" Not a float type" );
286
401
}
287
402
}
288
403
constexpr double as_boolean () const
289
404
{
290
- if (const bool *value = std::get_if< bool >(& data); value != nullptr ) {
405
+ if (const bool *value = data. get_if_boolean ( ); value != nullptr ) {
291
406
return *value;
292
407
} else {
293
408
throw std::runtime_error (" Not a boolean type" );
294
409
}
295
410
}
296
411
297
- constexpr std::string_view as_string () const
412
+ constexpr auto as_string () const
298
413
{
299
- if (const auto *value = std::get_if<std::string_view>(& data); value != nullptr ) {
414
+ if (const auto *value = data. get_if_string ( ); value != nullptr ) {
300
415
return *value;
301
416
} else {
302
417
throw std::runtime_error (" Not a string type" );
@@ -307,15 +422,15 @@ struct json
307
422
308
423
constexpr bool is_number_integer () const noexcept { return is_number_signed () || is_number_unsigned (); }
309
424
310
- constexpr bool is_number_signed () const noexcept { return std::holds_alternative<std:: int64_t >(data) ; }
425
+ constexpr bool is_number_signed () const noexcept { return data. selected == data_t ::selected_type::integer ; }
311
426
312
- constexpr bool is_number_unsigned () const noexcept { return std::holds_alternative<std:: uint64_t >(data) ; }
427
+ constexpr bool is_number_unsigned () const noexcept { return data. selected == data_t ::selected_type::uinteger ; }
313
428
314
- constexpr bool is_number_float () const noexcept { return std::holds_alternative< double >(data) ; }
429
+ constexpr bool is_number_float () const noexcept { return data. selected == data_t ::selected_type::floating_point ; }
315
430
316
- constexpr bool is_null () const noexcept { return std::holds_alternative<std::monostate>(data) ; }
431
+ constexpr bool is_null () const noexcept { return data. selected == data_t ::selected_type::empty ; }
317
432
318
- constexpr bool is_binary () const noexcept { return std::holds_alternative< binary_t >(data) ; }
433
+ constexpr bool is_binary () const noexcept { return data. selected == data_t ::selected_type::binary ; }
319
434
320
435
constexpr bool is_primitive () const noexcept
321
436
{
@@ -325,6 +440,10 @@ struct json
325
440
data_t data;
326
441
};
327
442
443
+ using json = basic_json<char >;
444
+ using object_t = basic_object_t <char >;
445
+ using value_pair_t = basic_value_pair_t <char >;
446
+ using array_t = basic_array_t <char >;
328
447
}// namespace constexpr_json
329
448
330
449
#endif
0 commit comments