-
Notifications
You must be signed in to change notification settings - Fork 2
/
proposal.bs
1467 lines (1089 loc) · 65.3 KB
/
proposal.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class='metadata'>
Title: A Minimal JSON Support Library for C++
Abstract: This paper proposes a minimal JSON support library for C++, which provides a simple and efficient way to represent and manipulate JSON data. The library consists of four main components: a `basic_json` class that represents a JSON value, a `basic_json_node` class that provide a type-erased JSON storage, and two slice classes that provides access and modification operations to the JSON class. The library aims to be compatible with the existing C++ standard library, and provides strong extension support.
Editor: Yexuan Xiao, bizwen@nykz.org
Editor: An Jiang, fvsja-cpp@outlook.com
Shortname: ????
Revision: 0
Status: D
Audience: LEWG
Group: WG21
Date: 2024-07-14
Markup Shorthands: markdown on
Markup Shorthands: biblio yes
ED: https://github.com/YexuanXiao/basic_json/blob/master/proposal.bs
</pre>
<style>
img {
width: 100%
}
@media (prefers-color-scheme: dark) {
img {
filter: invert(1) !important;
}
}
</style>
# Motivation
JSON is an internet standard, widely used for data transmission and storage, but the C++ standard library lacks support for JSON, which forces C++ users to choose among third-party libraries.
As the C++ standard evolves, C++ becomes more and more suitable for network programming, and C++'s high performance makes C++ equally suitable for processing large amounts of data stored by JSON, so adding JSON to the standard library is beneficial and harmless to C++.
There are many third-party libraries that provide JSON support for C++, but they have some drawbacks, such as:
- They may have external dependencies, which can increase the complexity and size of the project, and introduce potential errors and incompatibilities.
- They may have different interfaces, conventions, and styles, which can reduce the readability and consistency of the code, and increase the learning curve for new users.
- They may have different levels of features, performance, and compatibility, which can make it hard to choose the best one for a specific use case, and may require switching between different libraries for different scenarios.
- They may not follow the modern C++ standards and best practices, which can limit the usability and portability of the code, and prevent the use of new language features and idioms.
Therefore, this proposal aims to provide a minimal JSON support library for C++, which can address these issues, and offer the following benefits:
- It does not have any external dependencies, and can be easily integrated into any C++ project, without affecting the existing code or environment.
- It has a simple and consistent interface, which follows the existing C++ standard library conventions and styles, and can be easily learned and used by C++ programmers.
- It has a sufficient level of features, performance, and compatibility, which can cover most common use cases of JSON data, and can work with any conforming C++ compiler and platform.
- It is easy to implement, with only about 2200 lines of code, which avoids too much implementation details and problems.
# Proposal
I propose to add a `<json>` header and the following classes (templates): `nulljson_t`, `basic_json_node`, `basic_json`, `basic_const_json_slice`, `basic_json_slice`.
The `json_errc` enumeration and the `json_error` exception class are used to report errors.
```cpp
struct nulljson_t;
inline constexpr nulljson_t nulljson{};
template <typename Number,
typename Integer, typename UInteger, typename Allocator>
class basic_json_node;
template <typename Node, typename String,
typename Array, typename Object,
bool HasInteger, bool HasUInteger>
class basic_json;
template <typename Node, typename String,
typename Array, typename Object,
bool HasInteger, bool HasUInteger>
class basic_const_json_slice;
template <typename Node, typename String,
typename Array, typename Object,
bool HasInteger, bool HasUInteger>
class basic_json_slice;
enum class errc;
class json_error;
```
# Design
Since JSON has a self-referential structure ([[RFC8259]]), type erasure must be used.
<img src="https://www.json.org/img/object.png" alt="json.org’s JSON structure diagram: object">
<img src="https://www.json.org/img/value.png" alt="json.org’s JSON structure diagram: value">
## `nulljson`/`nulljson_t`
`nulljson` is a type similar to `nullopt`, used to indicate that the value of JSON is null.
`nulljson_t` is the type of `nulljson`. It is a trivial, empty class with a explicit default constructor, like all other construction tag types in the standard library.
## `basic_json_node`
`basic_json_node` has four template parameters: `Number`, `Integer`, `UInteger`, and `Allocator`. Users can use these template parameters to customize their preferred types and allocators.
For example, some users may prefer to use fixed-length integer types, some users may prefer to use integer types written in by C++ keywords, and the same for floating-point types.
`basic_json_node` holds an allocator and a tagged union that stores one of a boolean, number (floating-point), signed integer, unsigned integer, string, array (`vector`), or object (`map`), where the tag is represented by an enumeration value.
`basic_json_node` is a substitute for `basic_json`, providing storage space for `basic_json` in any situation where circular dependencies may occur.
`basic_json_node` is conceptually similar to a raw pointer. It does not always own memory, but can transfer memory through it.
Trivial copyability of `basic_json_node` depends on `Allocator`. If `Allocator` is trivially copyable, then so is `basic_json_node`, and `basic_json` arrays will get faster copy speed.
## `basic_json`
`basic_json` is a `semiregular` type that represents ownership of a JSON structure. It can be implemented as storing a `basic_json_node` as its only non-static data member, which makes `basic_json` and `basic_json_node` have the same size.
Its destructor is responsible for destructing all nodes and deallocate all dynamic storage held by the object. The copy constructor and copy assignment operator copy the JSON. The swap operation is provided as both a hidden friend function and a non-static member function.
The reason why the allocator is a template parameter of `basic_json_node` rather than `basic_json` is that `basic_json` must have the same size as `basic_json_node`, so `std::byte` is usually used to instantiate the allocator (`void` type can be used after LWG issue [[3917]] is resolved),
and then rebind is used to allocate storage. Once a specialization of `basic_json_node` is available, `basic_json` can be instantiated. `basic_json` has six template parameters: `Node`, `String`, `Array`, `Object`, `HasInteger`, `HasUInteger`.
`Node` must be a specialization of `basic_json_node`, and since `basic_json_node` provides type aliases to obtain the template arguments, `basic_json` can extract these type aliases, rewrite the specialization of `basic_json_node`, and compare it with `Node` to ensure this.
For arithmetic types, they are directly stored in the union, and since map and array store `basic_json_node`, pointers are needed to break the circular dependency,
and since the two types are not determined in `basic_json_node`, these pointers are actually of type `void*`. Conceptually, `basic_json` is a hybrid of container adapters and containers.
Although string type does not have circular dependency problem, `void*` is also used to save the space. The relationship between `basic_json` and `basic_json_node` is shown in the following figure:
<img src="https://raw.githubusercontent.com/YexuanXiao/basic_json/master/figure.png" alt="relationship diagram between node, json and slice">
Therefore, the `Allocator` template parameter of `basic_json_node` is not used directly, but always rebound to the allocators of string, array, and object.
Most of the member functions of `basic_json` are constructors, which make C++ values easily convertible to a JSON type.
In addition, `basic_json` also has a `&&`-qualified to `node_type` conversion function, which can transfer memory from `basic_json` to `basic_json_node`, just like `unique_ptr::release`.
The most special point of my proposal is to expose `basic_json_node`, which allows users to implement their own serializer and deserializer in a non-intrusive way: if a basic_json object that stores a boolean or arithmetic type value is needed, then construct it directly through the constructor,
if a `basic_json` object that stores an array or object type is needed, then users can construct array and object themselves, such as `std::vector<basic_json_node<>> a` and `std::map<std::string, basic_json_node> m`, then construct `basic_json` objects through the constructors of boolean or arithmetic types,
and then insert them into the container, finally, move `a` or `m` to the constructor of `basic_json`, and get a `basic_json` object that represents an array or map.
## `basic_json_slice`/`basic_const_json_slice`
`basic_json_slice` and `basic_const_json_slice` are similar to iterators of containers. `basic_const_json_slice` is constructible from a possibly const `basic_json` or `basic_json_node` and holds a pointer to that `basic_json_node` object. All non-static functions of `basic_const_json_slice` are `const`-qualified, and return either a value or a reference to a const object.
`basic_json_slice` has all the member functions that `basic_const_json_slice` has, and can be converted to `basic_const_json_slice`. In addition, `basic_const_json_slice` also has modifiers (overload of assignment operators), which can modify the value without changing the type of the value stored in the node.
`basic_json_slice` behaves like `propagate_const` in the Library Fundamentals TS ([[LFTSv3]]). Operations on a const `basic_json_slice` value also access the referred to node in a const manner, like a `basic_const_json_slice`. Only operations on a non-const `basic_json_slice` possibly modifies the node.
`basic_json_slice` and `basic_const_json_slice` are trivially copyable, so copying a slice has low overhead. No operation on a slice produces a copy of the `basic_json` object. For subscript operations, a new `basic_json_slice` or `basic_const_json_slice` is returned.
A default-constructed slice holds a null pointer, and thus all operations that need to queries or modifies the node results in undefined behavior on such a slice. The default-constructibility of slices is mainly used for default arguments.
## Summary
This design makes the `basic_json` template independent of the specific vector type, map type, string type. One can use `std::map`, `std::flat_map`, or `std::unordered_map` if desires.
Additionally, the string type as the `Key` of the Object and the `String` type as the `Value` can be different, which makes `KeyString` can be implemented with a dictionary that records all possible values.
This design does not care whether the string type has a charactor traits or allocator type.
In extreme cases, this design allows both strings to be `std::string_view`, such as when mapping the JSON byte stream to memory, each `std::string_view` references a part of the memory.
This makes it possible to not copy any strings (but still need to use dynamic memory to store maps and arrays).
# Implementation experience
I have provided a simple implementation on [Github](https://github.com/YexuanXiao/basic_json/blob/master/basic_json.hpp), the source code of the document is available in the same repository.
Currently, stateful allocators are not properly supported, because I do not have much experience with allocators. I have not provided serialization and deserialization functions, because the C++ standard library currently does not have a good IO API, and different users may have their own serialization and deserialization needs, so this is not part of the proposal.
The design is feasible and stable, but I need some feedback to appropriately increase the usability of the library.
# Use cases
```cpp
#include "basic_json.hpp"
#include <string_view>
#include <cassert>
int main()
{
using json = bizwen::json;
using node = bizwen::json_node;
using slice = bizwen::json_slice;
using const_slice = bizwen::const_json_slice;
auto null = bizwen::nulljson;
using namespace std::literals;
// A json object represents a json value. The default constructed json object does not hold any value, and its state is "undefined".
json j01{};
// Construct a json value with status "number" and value `1`.
json j02{ 1. };
// Construct a json value with status "true_value" and value `true`.
json j03{ true };
// Construct a json value with status "uinteger" and value `1`. This method is used to accurately store integers with more than 52 bits.
json j04{ 1ull };
// Construct a json value with status "string" and value `"abcdef"`.
json j05{ "abcdef" };
// Construct json with nullptr is forbidden because json does not have a pointer type and nullptr does not represent the empty string.
// json j05{ nullptr };
// Construct a json value with status "null" and value `nulljson`.
json j06{ null };
// Since initializer_list returns a reference to a const object, this method is inefficient.
// json j07{ json::array_type{ json{0}, json{1} } };
// json j08{ json::object_type{ { "key0"s, json{ 0 } }, { "key1"s, json{ 1 } } } };
// Use the helper class templates json::array and json::object for easy and efficient construction of json arrays and json objects.
// Constructs a json value with the state "array", containing two ordered values 0 and 1.
json j07{ json::array{ 0, 1 } };
// Construct a json value with state "object" such that `s08["key0"]==0` and `s08["key1"]==1` is true.
json j08{ json::object{ "key0"s, 0, "key1"s, 1 } };
// Copy a json object copies its stored state and values.
auto j09{ j08 };
// slice is an accessor and modifier of json values, and the default constructed slice is not associated with any json.
slice s01{};
// Use empty() to test if slice is associated with a json.
auto is_empty{ s01.empty() };
assert(is_empty); // does nothing
slice s07{ j07 }; // s07 is associated to j07
auto is_array{ s07.array() };
assert(is_array); // does nothing
// Convert a json value (s01) with state "undefined" to "integer" and set the value to `1`.
s01 = 1;
slice s02{ j02 }; // s02 is associated to j02
// Change the value of j02 to 2.f
s02 = 2.f;
// Sets the state of j02 to "undefined" and destroys the previous value.
s02.reset();
long long iv{ s07[0] };
// Iterate j07
for (auto i : s07.as_array())
{
assert(i.integer()); // does nothing
}
// Append a value to j07
// Due to CWG1996, list initialization cannot be used here.
json::array_type& arr( s07 );
arr.push_back(json{ 2 });
slice s08{ j08 };
// Iterate j08
for (auto const& [k, v] : s08.as_object())
{
assert(v.integer()); // does nothing
}
// Insert k-v pairs into j08
s08["key3"] = 2;
// Copying a slice is trivial
auto s08_1{ s08 };
// const_slice is similar to slice, but has only observers and never modifies the associated json.
const_slice c01{ s01 };
// Unlike slice, if the key does not exist in the object then a json_error exception is thrown.
try
{
c01["keyn"];
}
catch (bizwen::json_error const& je)
{
assert(je.code() == bizwen::json_errc::key_not_found); // does nothing
}
}
```
# Library wording
## JSON processing
[*Drafting note:* Add this subclause to [utilities]. -- *end drafting note*]
### Header `<json>` synopsis
```cpp
namespace std {
struct nulljson_t {
explicit nulljson_t() = default;
};
inline constexpr nulljson_t nulljson;
template<class Number = double, class Integer = long long, class UInteger = unsigned long long,
class Allocator = allocator<byte>>
class basic_json_node;
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
class basic_json;
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
class basic_json_slice;
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
class basic_const_json_slice;
using json = basic_json<>;
using json_node = basic_json_node<>;
using json_slice = basic_json_slice<>;
using const_json_slice = basic_const_json_slice<>;
enum class json_errc : <i>unspecified</i>;
class json_error;
namespace pmr {
template<class Number = double, class Integer = long long,
class UInteger = unsigned long long>
using basic_json_node = std::basic_json_node<Number, Integer, UInteger,
polymorphic_allocator<>>;
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
using basic_json = std::basic_json<Node, String, Array, Object, HasInteger, HasUInteger>;
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
using basic_json_slice = std::basic_json_slice<Node, String, Array, Object,
HasInteger, HasUInteger>;
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
using basic_const_json_slice = std::basic_const_json_slice<Node, String, Array, Object,
HasInteger, HasUInteger>;
using json = basic_json<>;
using json_node = basic_json_node<>;
using json_slice = basic_json_slice<>;
using const_json_slice = basic_const_json_slice<>;
}
}
```
### General
1. A `basic_json_node` object is a JSON node. A valid node object is always in one of the following states:
undefined, null, true, false, floating-point, signed integral, unsigned integral, string, array, and object.
2. Some operations of `basic_json` and `basic_json_slice` dynamically allocate memory.
This happens if and only if the `basic_json_node` held by `basic_json` or referenced by `basic_json_slice` is set to
the string, array, or object state. In such a case, let
- `A` be the `allocator_type` of the node type,
- `a` be the allocator object of type `A` stored in the node,
- `X` be the type for the state, which is `String` for the string state, `Array` for the array state, or `Object` for the object state.
- `AX` be `allocator_traits<A>::template rebind_alloc<X>`,
- `ax` be an lvalue of type `AX` that denotes an `AX` allocator object constructed from `a`.
Such an aforementioned operation allocates the dynamic storage with `allocator_traits<AX>::allocate(ax, 1)`,
and then constructs the `X` object using `allocator_traits<AX>::construct(ax, p, args...)`.
If an exception is thrown when setting the state, the value of the node is unchanged.
3. Likewise, the assignment operators and the destructor of `basic_json` destroy the object in the dynamic storage and then deallocate the storage when needed.
Let
- `x` be the `X` object to be destroyed,
- `pp` be `pointer_traits<allocator_traits<AX>::pointer>::pointer_to(x)`,
`x` is destroyed using `allocator_traits<AX>::destroy(ax, addressof(x))`,
and then the dynamic storage is deallocated with `allocator_traits<AX>::deallocate(pp, 1)`.
4. For the purpose of constraints of functions, the map type `Object` is considered transparently comparable if and only if
- *qualified-id*s `Object::key_compare` and `Object::key_compare::is_transparent` are all valid and denote types, or
- *qualified-id*s `Object::key_equal`, `Object::key_equal::is_transparent`, `Object::hasher`, and `Object::hasher::is_transparent`
are all valid and denote types.
### Class template `basic_json_node`
<pre highlight="c++">
namespace std {
template<class Number = double, class Integer = long long, class UInteger = unsigned long long,
class Allocator = allocator<byte>>
class basic_json_node {
private:
using <i>void-ptr</i> = allocator_traits<Allocator>::void_pointer; // exposition only
public:
using number_type = Number;
using integer_type = Integer;
using uinteger_type = UInteger;
using allocator_type = Allocator;
constexpr basic_json_node() noexcept(is_nothrow_default_constructible_v<Allocator>)
requires default_initializable<Allocator>;
constexpr explicit basic_json_node(const Allocator& a) noexcept;
constexpr basic_json_node(const basic_json_node& other) noexcept;
constexpr basic_json_node(basic_json_node&& other) noexcept;
constexpr basic_json_node& operator=(const basic_json_node& other)
noexcept(is_nothrow_copy_assignable_v<Allocator>);
constexpr basic_json_node& operator=(basic_json_node&& other)
noexcept(is_nothrow_move_assignable_v<Allocator>);
constexpr ~basic_json_node() noexcept;
};
template<class Number, class Integer, class UInteger, class Allocator, class Allocator2>
struct uses_allocator<basic_json_node<Number, Integer, UInteger, Allocator>, Allocator2> :
false_type {};
}
</pre>
1. *Mandates*:
- `floating_point<Number>`, `signed_integral<Integer>`, and `unsigned_integral<UInteger>` are all `true`.
- `Allocator` meets the *Cpp17Allocator* requirements, no diagnostic is required for this.
2. If the node is in the floating-point, signed integral, or unsigned integral state,
an object of type `number_type`, `integer_type`, or `uinteger_type` stored within the node.
If the node is in the string, array, or object state, an object of the type corresponding to the state is dynamically allocated,
and a <code><i>void-ptr</i></code> to it is stored within the node.
[*Note*: A single node type can refer to string, dynamic array, or map of different types. -- *end note*]
3. [*Note*: The `uses_allocator` partial specialization indicates that `basic_json_node` should not be uses-allocator constructed. -- *end note*]
#### Construction, assignment, and destruction
```cpp
constexpr basic_json_node() noexcept(is_nothrow_default_constructible_v<Allocator>)
requires default_initializable<Allocator>;
```
1. *Effects*: Initializes the node to the undefined state. Value-initializes the stored allocator.
```cpp
constexpr explicit basic_json_node(const Allocator& a) noexcept;
```
2. *Effects*: Initializes the node to the undefined state. Copy-constructs the stored allocator from `a`.
```cpp
constexpr basic_json_node(const basic_json_node& other) noexcept;
constexpr basic_json_node(basic_json_node&& other) noexcept;
```
3. *Effects*:
- For the copy constructor, copy-constructs the stored allocator from that of `other`; for the move constructor, move construction is used instead.
- Sets `*this` in the same state as `other`.
- If `other` stores a numeric value, stores the same value in `this`.
- Otherwise, if `other` stores a <code><i>void-ptr</i></code> to an allocated object,
for the copy constructor, copy-constructs that pointer into `*this`; for the move constructor, move construction is used instead.
4. *Remarks*:
- The copy constructor is trivial if both `is_trivially_copy_constructible_v<Allocator>` and <code>is_trivially_copy_constructible_v<<i>void-ptr</i>></code> are `true`.
- The move constructor is trivial if both `is_trivially_move_constructible_v<Allocator>` and <code>is_trivially_move_constructible_v<<i>void-ptr</i>></code> are `true`.
```cpp
constexpr basic_json_node& operator=(const basic_json_node& other)
noexcept(is_nothrow_copy_assignable_v<Allocator>);
constexpr basic_json_node& operator=(basic_json_node&& other)
noexcept(is_nothrow_move_assignable_v<Allocator>);
```
5. *Constraints*: For the move assignment operator, `is_move_assignable_v<Allocator>` is `true`.
6. *Effects*:
- If `this == addressof(other)`, does nothing.
- Otherwise, copy- or move-assign the stored allocator of `*this` from that of `other`, for copy or move assignment operator respectively. And then
- if `*this` is in the same state as `other` and stores a numeric value or <code><i>void-ptr</i></code>,
copy- or move-assign the stored numeric value or <code><i>void-ptr</i></code> in `other` to that of `*this`, for copy or move assignment operator respectively, otherwise,
- destroys the stored a numeric value or <code><i>void-ptr</i></code>, if any, and then
copy- or move-constructs the numeric value or <code><i>void-ptr</i></code> from that of `other` for copy or move assignment operator respectively, if any.
7. *Returns*: `*this`.
8. *Remarks*:
- The copy assignment operator is deleted if `is_copy_assignable_v<Allocator>` is `false`.
- The copy assignment operator is trivial if `is_trivially_copy_assignable_v<Allocator>`, <code>is_trivially_destructible_v<<i>void-ptr</i>></code>, <code>is_trivially_copy_constructible_v<<i>void-ptr</i>></code>, and <code>is_trivially_copy_assignable_v<<i>void-ptr</i>></code> are all `true`.
- The move assignment operator is trivial if `is_trivially_move_assignable_v<Allocator>`, <code>is_trivially_destructible_v<<i>void-ptr</i>></code>, <code>is_trivially_move_constructible_v<<i>void-ptr</i>></code>, and <code>is_trivially_move_assignable_v<<i>void-ptr</i>></code> are all `true`.
```cpp
constexpr ~basic_json_node() noexcept;
```
9. *Effects*: Destroys the stored allocator, and the numeric value or the <code><i>void-ptr</i></code>, if any. [*Note*: No deallocation happens. -- *end note*]
10. *Remarks*: The destructor is trivial if both `is_trivially_destructible_v<Allocator>` and <code>is_trivially_destructible_v<<i>void-ptr</i>></code> are `true`.
### Class template `basic_json`
<pre highlight="c++">
namespace std {
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
class basic_json {
public:
static constexpr bool has_integer = HasInteger;
static constexpr bool has_uinteger = HasUInteger;
using slice_type = basic_json_slice<Node, String, Array, Object,
HasInteger, HasUInteger>;
using const_slice_type = basic_const_json_slice<Node, String, Array, Object,
HasInteger, HasUInteger>;
using number_type = node_type::number_type;
using integer_type = node_type::integer_type;
using uinteger_type = node_type::uinteger_type;
using char_type = string_type::value_type;
using map_node_type = object_type::value_type;
using allocator_type = node_type::allocator_type;
using key_string_type = object_type::key_type;
using key_char_type = key_string_type::value_type;
constexpr basic_json() requires default_initializable<allocator_type> = default;
constexpr explicit basic_json(const allocator_type& a) noexcept : <i>node_</i>(a) {}
constexpr basic_json(const basic_json& rhs);
constexpr basic_json(basic_json&& rhs) noexcept;
constexpr basic_json& operator=(const basic_json& rhs);
constexpr basic_json& operator=(basic_json&& rhs)
noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
allocator_traits<Allocator>::is_always_equal::value);
constexpr ~basic_json();
constexpr void swap(basic_json& rhs)
noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
allocator_traits<Allocator>::is_always_equal::value);
friend constexpr void swap(basic_json& lhs, basic_json& rhs)
noexcept(noexcept(lhs.swap(rhs)));
constexpr basic_json(const basic_json& rhs, const allocator_type& a);
constexpr basic_json(basic_json&& rhs, const allocator_type& a)
noexcept(allocator_traits<Allocator>::is_always_equal::value);
constexpr basic_json(nulljson_t, const allocator_type& a = allocator_type()) noexcept;
template<class T>
constexpr basic_json(T n, const allocator_type& a = allocator_type()) noexcept;
constexpr explicit basic_json(string_type v, const allocator_type& a = allocator_type());
constexpr basic_json(const char_type* first, const char_type* last,
const allocator_type& a = allocator_type());
constexpr basic_json(const char_type* str, string_type::size_type count,
const allocator_type& a = allocator_type());
constexpr explicit basic_json(const char_type* str,
const allocator_type& a = allocator_type());
template<class StrLike>
constexpr basic_json(const StrLike& str, const allocator_type& a = allocator_type());
constexpr explicit basic_json(array_type arr,
const allocator_type& a = allocator_type());
constexpr explicit basic_json(object_type obj,
const allocator_type& a = allocator_type());
constexpr explicit basic_json(node_type&& n) noexcept;
constexpr explicit basic_json(node_type&& n, const allocator_type& a) noexcept;
basic_json(nullptr_t, const allocator_type& = allocator_type()) = delete;
constexpr allocator_type get_allocator() const noexcept;
constexpr operator node_type() && noexcept;
constexpr slice_type slice() noexcept;
constexpr const_slice_type slice() const noexcept;
private:
node_type <i>node_</i>; // exposition only
// Helper Classes
template<class... Ts>
struct <i>array</i>; // exposition only
template<size_t N>
struct <i>object</i>; // exposition only
template<class... Ts>
<i>object</i>(Ts&&...) -> <i>object</i><<i>see below</i>>;
};
}
</pre>
1. *Mandates*:
- `Node` is a well-formed specialization of `basic_json_node`.
- *qualified-id* `Array::value_type` is valid and denotes the same type as `Node`.
- *qualified-id* `Object::mapped_type` is valid and denotes the same type as `Node`.
<h5 id=construction-and-swap-basic-json>Construction and swap</h4>
1. Every constructor taking a `const allocator_type&` parameter constructs <i>`node_`</i> from that parameter.
2. Every constructor or assignment operator taking a `basic_json&&` parameter leaves the argument in a valid but unspecified state after construction or assignment.
Except for the move constructor, each of these functions reuse the dynamic storage of the argument if and only if
there is some dynamic storage allocated and the stored allocator in the <i>`node_`</i> is equal to that of the argument.
3. The copy constructors, move constructor, copy assignment operator, move assignment operator, and `swap` functions propagate the allocator value as specified in [container.alloc.reqmts].
```cpp
constexpr basic_json(nulljson_t, const allocator_type& a = allocator_type()) noexcept;
```
4. *Effects*: Sets <i>`node_`</i> to the null state.
```cpp
template<class T>
constexpr basic_json(T n, const allocator_type& a = allocator_type()) noexcept;
```
5. *Constraints*: `is_arithmetic_v<T>` is `true`.
6. *Effects*:
- If `T` is `bool`, sets <i>`node_`</i> to the true or the false state if `n` is `true` or `false`, respectively.
- Otherwise, if `signed_integral<T>` and `has_integer` are both `true`, sets <i>`node_`</i> to the signed integral state and stores `n`.
- Otherwise, if `unsigned_integral<T>` and `has_uinteger` are both `true`, sets <i>`node_`</i> to the unsigned integral state and stores `n`.
- Otherwise, sets <i>`node_`</i> to the floating-point state and stores `static_cast<number_type>(n)`.
```cpp
constexpr explicit basic_json(string_type v, const allocator_type& a = allocator_type());
```
7. *Effects*: Sets <i>`node_`</i> to the string state and stores a string constructed from `std::move(v)`.
8. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`.
```cpp
constexpr basic_json(const char_type* first, const char_type* last, const allocator_type& a = allocator_type());
```
9. *Preconditions*: [`first`, `last`) is a valid range.
10. *Effects*: Sets <i>`node_`</i> to the string state and stores a string containing characters in [`first`, `last`).
11. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`.
```cpp
constexpr basic_json(const char_type* str, string_type::size_type count, const allocator_type& a = allocator_type());
```
12. *Preconditions*: [`str`, `str + count`) is a valid range.
13. *Effects*: Sets <i>`node_`</i> to the string state and stores a string containing characters in [`str`, `str + count`).
14. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`.
```cpp
constexpr explicit basic_json(const char_type* str, const allocator_type& a = allocator_type());
```
15. *Preconditions*: `str` points to a null-terminated `char_type` sequence.
16. *Effects*: Sets <i>`node_`</i> to the string state and stores a string containing characters in [`str`, `str + count`),
where `count` is the number of characters in the null-terminated sequence.
17. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`.
```cpp
template<class StrLike>
constexpr basic_json(const StrLike& str, const allocator_type& a = allocator_type());
```
18. *Constraints*:
- `constructible_from<string_type, StrLike>` is `true`, and
- `is_convertible_v<const KeyStrLike&, const key_char_type*>` is `false`.
19. *Effects*: Sets <i>`node_`</i> to the string state and stores a string constructed from `str`.
20. *Throws*: The exception thrown on allocation failure, or the exception thrown in by the construction of the `String`.
```cpp
constexpr explicit basic_json(array_type arr, const allocator_type& a = allocator_type());
```
21. *Effects*: Sets <i>`node_`</i> to the array state and stores a dynamic array constructed from `std::move(arr)`.
22. *Throws*: The exception thrown on allocation failure, or the exception thrown by the construction of the `Array`.
```cpp
constexpr explicit basic_json(object_type obj, const allocator_type& a = allocator_type());
```
23. *Effects*: Sets <i>`node_`</i> to the object state and stores a map constructed from `std::move(obj)`.
24. *Throws*: The exception thrown on allocation failure, or the exception thrown by the construction of the `Object`.
```cpp
constexpr explicit basic_json(node_type&& n) noexcept;
```
25. *Effects*: Constructs <i>`node_`</i> from `std::move(n)` and sets `n` to the undefined state.
The stored allocator in `n` is unchanged.
```cpp
constexpr explicit basic_json(node_type&& n, const allocator_type& a) noexcept;
```
25. *Effects*: Constructs <i>`node_`</i> from `std::move(a)`, sets it to the same state as `n`, and then sets `n` to the undefined state.
The stored allocator in `n` is unchanged.
```cpp
constexpr void swap(basic_json& rhs)
noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
allocator_traits<Allocator>::is_always_equal::value);
friend constexpr void swap(basic_json& lhs, basic_json& rhs) noexcept(noexcept(lhs.swap(rhs)));
```
26. *Effects*: Swaps the values of the <i>`node_`</i> member of both `basic_json` objects.
<h5 id=observers-basic-json>Observers</h5>
```cpp
constexpr allocator_type get_allocator() const noexcept;
```
1. *Returns*: The copy of the constructor stored in <i>`node_`</i>.
#### Slicing
```cpp
constexpr operator node_type() && noexcept;
```
1. *Effects*: Sets <i>`node_`</i> to the undefined state.
2. *Returns*: A `node_type` value holding the value of <i>`node_`</i> before modification.
3. *Recommended practice*: An implementation should emit a diagnostic message when the return value is discarded.<br>
[*Note*: Discarding the return value leaks memory if <i>`node_`</i> was originally in the string, array, or object state. -- *end note*]
```cpp
constexpr slice_type slice() noexcept;
```
4. *Effects*: Equivalent to `return slice_type(*this);`.
```cpp
constexpr const_slice_type slice() const noexcept;
```
5. *Effects*: Equivalent to `return const_slice_type(*this);`.
### Class template `basic_json_slice`
<pre highlight="c++">
namespace std {
template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
class basic_json_slice {
public:
static constexpr bool has_integer = HasInteger;
static constexpr bool has_uinteger = HasUInteger;
using node_type = Node;
using object_type = Object;
using value_type = Node;
using array_type = Array;
using string_type = String;
using slice_type = basic_json_slice;
using const_slice_type = basic_const_json_slice<Node, String, Array, Object,
HasInteger, HasUInteger>;
using json_type = basic_json<Node, String, Array, Object,
HasInteger, HasUInteger>;
using number_type = node_type::number_type;
using integer_type = node_type::integer_type;
using uinteger_type = node_type::uinteger_type;
using char_type = string_type::value_type;
using map_node_type = object_type::value_type;
using allocator_type = node_type::allocator_type;
using key_string_type = object_type::key_type;
using key_char_type = key_string_type::value_type;
constexpr basic_json_slice() noexcept = default;
constexpr basic_json_slice(json_type& j) noexcept;
constexpr basic_json_slice(node_type& n) noexcept;
constexpr void swap(basic_json_slice& rhs) noexcept;
friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept;
constexpr basic_json_slice& operator=(json_type& j) noexcept;
constexpr basic_json_slice& operator=(node_type& n) noexcept;
constexpr bool empty() const noexcept;
constexpr bool undefined() const noexcept;
constexpr bool string() const noexcept;
constexpr bool null() const noexcept;
constexpr bool boolean() const noexcept;
constexpr bool number() const noexcept;
constexpr bool object() const noexcept;
constexpr bool array() const noexcept;
constexpr bool integer() const noexcept;
constexpr bool uinteger() const noexcept;
constexpr explicit operator bool() const;
constexpr explicit operator number_type() const;
constexpr explicit operator nulljson_t() const;
constexpr explicit operator const string_type&() const&;
constexpr explicit operator const array_type&() const&;
constexpr explicit operator const object_type&() const&;
constexpr explicit operator integer_type() const;
constexpr explicit operator uinteger_type() const;
template<integral I>
constexpr basic_const_json_slice operator[](I pos) const;
constexpr basic_const_json_slice operator[](const key_string_type& k) const;
template<class KeyStrLike>
constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
constexpr void reset() noexcept;
constexpr basic_json_slice& operator=(nulljson_t);
template<class T>
constexpr basic_json_slice& operator=(T n);
constexpr basic_json_slice& operator=(const string_type& str);
constexpr basic_json_slice& operator=(string_type&& str);
constexpr basic_json_slice& operator=(const char_type* str);
template<class StrLike>
constexpr basic_json_slice& operator=(const StrLike& str);
template<integral I>
constexpr basic_json_slice operator[](I pos);
constexpr basic_json_slice operator[](const key_string_type& k);
template<class KeyStrLike>
constexpr basic_json_slice operator[](const KeyStrLike& k);
constexpr basic_json_slice operator[](const key_char_type* ks);
private:
node_type* <i>node_</i> = nullptr; // exposition only
};
template<class Node, class String, class Array, class Object,
bool HasInteger, bool HasUInteger, class Allocator>
struct uses_allocator<basic_json_slice<Node, String, Array, Object,
HasInteger, HasUInteger>, Allocator2> :
false_type {};
}
</pre>
1. Every specialization of `basic_json_slice` is a trivally copyable class that models `semiregular`.
2. A `basic_json_slice` is considered valid if its <i>`node_`</i> member points to a `node_type` object within its lifetime.<br>
[*Note*: A default-constructed `basic_json_slice` is not valid. -- *end note*]
3. [*Note*: The default constructor of `basic_json_slice` is intentionally used for default arguments. -- *end note*]
4. Whenever `basic_json_slice` is valid, its referenced node is the object denoted by <code>*<i>node_</i></code>.
5. [*Note*: The `uses_allocator` partial specialization indicates that `basic_json_slice` should not be uses-allocator constructed. -- *end note*]
<h5 id=construction-and-swap-basic-json-slice>Construction and swap</h4>
```cpp
constexpr basic_json_slice(json_type& j) noexcept;
```
1. *Effects*: Initializes <i>`node_`</i> with <code>addressof(j.<i>node_</i>)</code>.
```cpp
constexpr basic_json_slice(node_type& n) noexcept;
```
2. *Effects*: Initializes <i>`node_`</i> with `addressof(n)`.
```cpp
constexpr void swap(basic_json_slice& rhs) noexcept;
friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept;
```
3. *Effects*: Swaps the values of the <i>`node_`</i> member of both `basic_json_slice` objects.
#### Rebinding
```cpp
constexpr basic_json_slice& operator=(json_type& j) noexcept;
```
1. *Effects*: Equivalent to `return *this = basic_json_slice{j};`.
```cpp
constexpr basic_json_slice& operator=(node_type& n) noexcept;
```
2. *Effects*: Equivalent to `return *this = basic_json_slice{n};`.
<h5 id=observers-basic-json-slice>Observers</h5>
<pre highlight="c++">
constexpr bool empty() const noexcept;
constexpr bool undefined() const noexcept;
constexpr bool string() const noexcept;
constexpr bool null() const noexcept;
constexpr bool boolean() const noexcept;
constexpr bool number() const noexcept;
constexpr bool object() const noexcept;
constexpr bool array() const noexcept;
constexpr bool integer() const noexcept;
constexpr bool uinteger() const noexcept;
constexpr explicit operator bool() const;
constexpr explicit operator number_type() const;
constexpr explicit operator nulljson_t() const;
constexpr explicit operator const string_type&() const&;
constexpr explicit operator const array_type&() const&;
constexpr explicit operator const object_type&() const&;
constexpr explicit operator integer_type() const;
constexpr explicit operator uinteger_type() const;
template<integral I>
constexpr basic_const_json_slice operator[](I pos) const;
constexpr basic_const_json_slice operator[](const key_string_type& k) const;
template<class KeyStrLike>
constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
</pre>
1. Every observer function of `basic_json_slice` has the same constraints and semantics as converting the `basic_json_slice` to its `const_slice_type`
and then calling the corresponding member function of `basic_const_json_slice`.
#### Modifiers
1. Except for `reset`, a modifier function of `basic_json_slice` may change the state of the referenced node, but only if the node was in the undefined state.
A `json_error` exception is thrown if one attempts to change the established non-undefined state of the node.
```cpp
constexpr void reset() noexcept;
```
2. *Preconditions*: `*this` is valid.
3. *Effects*: Sets the referenced node to the undefined state.
```cpp
constexpr basic_json_slice& operator=(nulljson_t);
```
4. *Preconditions*: `*this` is valid.
5. *Effects*:
- If the referenced node is in the undefined state, sets it to the null state.
- Otherwise, if the referenced node is in the null state, does nothing.
6. *Returns*: `*this`.
7. *Throws*: A `json_error` exception constructed from `json_errc::not_undefined_or_null` if the referenced node is not in the undefined or the null state.
```cpp
template<class T>
constexpr basic_json_slice& operator=(T n);
```
8. *Constraints*: `is_arithmetic_v<T>` is `true`.
9. *Preconditions*: `*this` is valid.
10. *Effects*:
- If the referenced node is in the undefined state, performs <code>*<i>node_</i> = json_type{n}</code>.
- Otherwise, if
- `T` is `bool`, `n` is `true`, and the referenced node is in the true state, or
- `T` is `bool`, `n` is `false`, and the referenced node is in the false state,
then does nothing.
- Otherwise, if `HasInteger` and `signed_integral<T>` are both `true` and the referenced node is in the signed integral state,
replaces the stored signed integer value with `n`.
- Otherwise, if `HasUInteger` and `unsigned_integral<T>` are both `true` and the referenced node is in the unsigned integral state,
replaces the stored unsigned integer value with `n`.
- Otherwise, the referenced node is in the floating-point state, replaces the stored floating-point value with `static_cast<number_type>(n)`.
- Otherwise, throws a `json_error` exception constructed from `json_errc::not_undefined_or_number`;
11. *Returns*: `*this`.
```cpp
constexpr basic_json_slice& operator=(const string_type& str);
```
12. *Preconditions*: `*this` is valid.
13. *Effects*:
- If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from `str`.
- Otherwise, if the referenced node is in the string state, assigns `str` to the stored string.
14. *Returns*: `*this`.
15. *Throws*:
- A `json_error` exception constructed from `json_errc::not_undefined_or_string` if the referenced node is not in the undefined or the string state.
- Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`.
```cpp
constexpr basic_json_slice& operator=(string_type&& str);
```
16. *Preconditions*: `*this` is valid.
17. *Effects*:
- If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from `std::move(str)`.
- Otherwise, if the referenced node is in the string state, assigns `std::move(str)` to the stored string.
18. *Returns*: `*this`.
19. *Throws*:
- A `json_error` exception constructed from `json_errc::not_undefined_or_string` if the referenced node is not in the undefined or the string state.
- Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`.
```cpp
constexpr basic_json_slice& operator=(const char_type* str);
```
20. *Preconditions*: `*this` is valid. If the referenced node is in the undefined or the string state, `str` points to a null-terminated `char_type` sequence.
21. *Effects*:
- If the referenced node is in the undefined state, sets it to the string state and stores a string containing characters from `str` until `str + count`,
where `count` is the number of characters in the null-terminated sequence.
- Otherwise, if the referenced node is in the string state, replaces the contents of `str` with that of the null-terminated sequence.
22. *Returns*: `*this`.
23. *Throws*:
- A `json_error` exception constructed from `json_errc::not_undefined_or_string` if the referenced node is not in the undefined or the string state.
- Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`.
```cpp
template<class StrLike>
constexpr basic_json_slice& operator=(const StrLike& str);
```
24. *Constraints*:
- `constructible_from<string_type, StrLike>` is `true`, and
- `is_convertible_v<const KeyStrLike&, const key_char_type*>` is `false`.
25. *Preconditions*: `*this` is valid.
26. *Effects*:
- If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from `str`.
- Otherwise, if the referenced node is in the string state, assigns `str` to the stored string.
27. *Returns*: `*this`.