├── .gitignore ├── .git-blame-ignore-revs ├── CMakeLists.txt ├── .clang-format ├── README.md ├── usecases.cpp ├── LICENSE ├── figure.drawio ├── figure.svg ├── basic_json.hpp └── proposal.bs /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .cache 3 | build -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # clang-format 2 | 85e921989693bbc26b162eb8bb104295c5a0e270 3 | 953ae87a62e2b68ad642470dd4b7e794f2abfe4f 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(basic_json) 4 | 5 | set(CMAKE_CXX_STANDARD 23) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | 9 | enable_testing() 10 | 11 | add_executable(usecases usecases.cpp) 12 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | BreakBeforeBraces: Custom 4 | BraceWrapping: 5 | AfterCaseLabel: false 6 | AfterClass: true 7 | AfterControlStatement: Always 8 | AfterEnum: true 9 | AfterExternBlock: true 10 | AfterFunction: true 11 | AfterNamespace: true 12 | AfterObjCDeclaration: true 13 | AfterStruct: true 14 | AfterUnion: true 15 | BeforeCatch: true 16 | BeforeElse: true 17 | BeforeLambdaBody: false 18 | BeforeWhile: false 19 | IndentBraces: false 20 | SplitEmptyFunction: true 21 | SplitEmptyRecord: true 22 | SplitEmptyNamespace: true 23 | BreakTemplateDeclarations: Yes 24 | DeriveLineEnding: false 25 | NamespaceIndentation: All 26 | SortIncludes: false 27 | SpaceBeforeCpp11BracedList: false 28 | SpaceBeforeInheritanceColon: false 29 | SpaceInEmptyBlock: false 30 | UseCRLF: true 31 | UseTab: ForIndentation 32 | IndentWidth: 4 33 | TabWidth: 4 34 | IndentAccessModifiers: false 35 | ColumnLimit: 120 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modern C++ JSON Library 2 | 3 | This is a modern C++ library for working with JSON data. It aims to provide full support for the JSON standard, as well as allowing users to customize and extend the library according to their needs. The library offers a user-friendly and C++-idiomatic API, without compromising on performance. 4 | 5 | This branch is the v2 version and is under development. For the older version, please visit the [master](https://github.com/YexuanXiao/basic_json/tree/master) branch. 6 | 7 | ## Goals 8 | 9 | - Supports JSON Pointer, JSON Patch 10 | - Supports UTF-8, UTF-16, and UTF-32 encodings 11 | - Supports custom number type and integer extensions 12 | - Supports JSON parsing, serialization, and manipulation 13 | - Supports pretty-printing, indentation, and escaping options 14 | - Supports user-defined types, custom allocators, and custom serializers 15 | 16 | - Use exceptions and exception safety 17 | - Use herbceptions when it is available 18 | 19 | ## Not Goals 20 | 21 | - INF and NAN 22 | - Reading comments 23 | - Other extensions 24 | 25 | ## Roadmap 26 | 27 | Stage 1: JSON representation, accessors and modifiers 28 | 29 | Stage 2: Serializer and deserializer 30 | 31 | Stage 3: JSON Pointer and Patch 32 | 33 | ## Proposal draft 34 | 35 | https://storage.nykz.org/proposals/minimal-json/ 36 | 37 | ![figure](https://raw.githubusercontent.com/YexuanXiao/basic_json/v2/figure.svg) 38 | -------------------------------------------------------------------------------- /usecases.cpp: -------------------------------------------------------------------------------- 1 | #include "basic_json.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class simple_json_node 9 | { 10 | // LWG3917 https://cplusplus.github.io/LWG/issue3917 11 | // The standard is not clear on whether allocators support incomplete types, 12 | // but all current active implementations support this. 13 | using variant_type = std::variant*, std::map*>; 15 | using allocator_type = std::allocator; 16 | 17 | template 18 | friend class bizwen::basic_json; 19 | template 20 | friend class bizwen::basic_json_slice; 21 | template 22 | friend class bizwen::basic_const_json_slice; 23 | 24 | simple_json_node() = default; 25 | 26 | constexpr simple_json_node(allocator_type const& a) 27 | : alloc(a) 28 | { 29 | } 30 | 31 | variant_type stor; 32 | allocator_type alloc; 33 | }; 34 | 35 | int main() 36 | { 37 | using node = simple_json_node; 38 | using json = bizwen::basic_json; 39 | using slice = bizwen::basic_json_slice; 40 | using const_slice = bizwen::basic_const_json_slice; 41 | auto null = bizwen::nulljson; 42 | using namespace std::literals; 43 | // A json object represents a json value. The default constructed json object does not hold any value, and its state 44 | // is "undefined". 45 | json j01{}; 46 | // Construct a json value with status "number" and value `1`. 47 | json j02{ 1. }; 48 | // Construct a json value with status "true_value" and value `true`. 49 | json j03{ true }; 50 | // Construct a json value with status "uinteger" and value `1`. This method is used to accurately store integers 51 | // with more than 52 bits. 52 | json j04{ 1ull }; 53 | // Construct a json value with status "string" and value `"abcdef"`. 54 | json j05{ "abcdef" }; 55 | // Construct json with nullptr is forbidden because json does not have a pointer type and nullptr does not represent 56 | // the empty string. json j05{ nullptr }; Construct a json value with status "null" and value `nulljson`. 57 | json j06{ null }; 58 | // Since initializer_list returns a reference to a const object, this method is inefficient. 59 | // json j07{ json::array_type{ json{0}, json{1} } }; 60 | // json j08{ json::object_type{ { "key0"s, json{ 0 } }, { "key1"s, json{ 1 } } } }; 61 | // Use the helper class templates json::array and json::object for easy and efficient construction of json arrays 62 | // and json objects. Constructs a json value with the state "array", containing two ordered values 0 and 1. 63 | json j07{ json::array{ 0, 1 } }; 64 | // Construct a json value with state "object" such that `s08["key0"]==0` and `s08["key1"]==1` is true. 65 | json j08{ json::object{ "key0"s, 0, "key1"s, 1 } }; 66 | // Copy a json object copies its stored state and values. 67 | auto j09{ j08 }; 68 | 69 | // slice is an accessor and modifier of json values, and the default constructed slice is not associated with any 70 | // json. 71 | slice s01{}; 72 | // Use empty() to test if slice is associated with a json. 73 | auto is_empty{ s01.empty() }; 74 | assert(is_empty); // does nothing 75 | slice s07{ j07 }; // s07 is associated to j07 76 | auto is_array{ s07.array() }; 77 | assert(is_array); // does nothing 78 | // Convert a json value (s01) with state "undefined" to "integer" and set the value to `1`. 79 | s01 = 1; 80 | slice s02{ j02 }; // s02 is associated to j02 81 | // Change the value of j02 to 2.f 82 | s02 = 2.f; 83 | // Sets the state of j02 to "undefined" and destroys the previous value. 84 | s02.reset(); 85 | long long iv{ s07[0] }; 86 | 87 | // Iterate j07 88 | for (auto i : s07.as_array()) 89 | { 90 | assert(i.integer()); // does nothing 91 | } 92 | // Append a value to j07 93 | // Due to CWG1996, list initialization cannot be used here. 94 | json::array_type& arr(s07); 95 | arr.push_back(json{ 2 }); 96 | slice s08{ j08 }; 97 | // Iterate j08 98 | for (auto const& [k, v] : s08.as_object()) 99 | { 100 | assert(v.integer()); // does nothing 101 | } 102 | // Insert k-v pairs into j08 103 | s08["key3"] = 2; 104 | // Copying a slice is trivial 105 | auto s08_1{ s08 }; 106 | 107 | // const_slice is similar to slice, but has only observers and never modifies the associated json. 108 | const_slice c01{ s01 }; 109 | // Unlike slice, if the key does not exist in the object then a json_error exception is thrown. 110 | try 111 | { 112 | c01["keyn"]; 113 | } 114 | catch (bizwen::json_error const& je) 115 | { 116 | assert(je.code() == bizwen::json_errc::key_not_found); // does nothing 117 | } 118 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /figure.drawio: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /figure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
basic_json
<Node>






basic_json...
Node:
node_
Node:...
alloc: Allocator
alloc: Allocator

stor: variant<Ts...> 
stor: variant<Ts...> 
Number
Number
StringView/String*
StringView/String*
Array*
Array*
Object*
Object*
UInteger
UInteger
Integer
Integer
String
String
Vector<Node>
Vector<Node>
Map<Node>
Map<Node>
Members:
Members:
basic_ json_splice
<Node>
/
 basic_const_json_splice
<Node>
basic_ json_splice...
node_ptr: Node*
node_ptr: Node*
reference
referen...
storage
storage
kind_tundefinednullboolnumberintegeruintegerstringarrayobject
std::monostate
std::monosta...
nulljson_t
nulljson_t
bool
bool
-------------------------------------------------------------------------------- /basic_json.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BIZWEN_BASIC_JSON_HPP 2 | #define BIZWEN_BASIC_JSON_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // https://www.rfc-editor.org/rfc/rfc8259 16 | 17 | namespace bizwen 18 | { 19 | struct nulljson_t 20 | { 21 | explicit constexpr nulljson_t() noexcept = default; 22 | }; 23 | 24 | struct disable_integer_t 25 | { 26 | explicit constexpr disable_integer_t() noexcept = default; 27 | }; 28 | 29 | struct disable_uinteger_t 30 | { 31 | explicit constexpr disable_uinteger_t() noexcept = default; 32 | }; 33 | 34 | inline constexpr nulljson_t nulljson{}; 35 | 36 | template 37 | class basic_json; 38 | 39 | template 40 | class basic_const_json_slice; 41 | 42 | template 43 | class basic_json_slice; 44 | 45 | enum class json_errc 46 | { 47 | // for accessor 48 | is_undefined = 1, 49 | not_null, 50 | not_boolean, 51 | not_number, 52 | not_integer, 53 | not_uinteger, 54 | not_string, 55 | not_array, 56 | not_object, 57 | nonarray_indexing, 58 | nonobject_indexing, 59 | key_not_found, 60 | // for modifier 61 | is_empty, 62 | not_undefined_or_null, 63 | not_undefined_or_boolean, 64 | not_undefined_or_number, 65 | not_undefined_or_integer, 66 | not_undefined_or_uinteger, 67 | not_undefined_or_string, 68 | not_undefined_or_array, 69 | not_undefined_or_object, 70 | // for deserializer 71 | syntax_error, 72 | number_overflow, 73 | // for serializer 74 | number_nan, 75 | number_inf 76 | }; 77 | 78 | class json_error: public std::runtime_error 79 | { 80 | public: 81 | json_error(json_errc ec) 82 | : std::runtime_error("") 83 | , code_(ec) 84 | { 85 | } 86 | 87 | json_errc code() const noexcept { return code_; } 88 | 89 | char const* what() const noexcept override 90 | { 91 | switch (code_) 92 | { 93 | case json_errc::not_null: 94 | return "JSON error: value isn't a null."; 95 | case json_errc::not_boolean: 96 | return "JSON error: value not a boolean."; 97 | case json_errc::not_integer: 98 | return "JSON error: value isn't an integer."; 99 | case json_errc::not_uinteger: 100 | return "JSON error: value isn't an unsigned integer."; 101 | case json_errc::not_number: 102 | return "JSON error: value isn't a number."; 103 | case json_errc::not_string: 104 | return "JSON error: value isn't a string."; 105 | case json_errc::not_array: 106 | return "JSON error: value isn't an array."; 107 | case json_errc::not_object: 108 | return "JSON error: value isn't an object."; 109 | case json_errc::nonarray_indexing: 110 | return "JSON error: value isn't an array but is accessed using operator[]."; 111 | case json_errc::nonobject_indexing: 112 | return "JSON error: value isn't an object but is accessed using operator[]."; 113 | case json_errc::key_not_found: 114 | return "JSON error: key does not exist."; 115 | case json_errc::not_undefined_or_null: 116 | return "JSON error: current value is not undefined or not null."; 117 | case json_errc::not_undefined_or_boolean: 118 | return "JSON error: current value is not undefined or not bool."; 119 | case json_errc::not_undefined_or_number: 120 | return "JSON error: current value is not undefined or not a number."; 121 | case json_errc::not_undefined_or_string: 122 | return "JSON error: current value is not undefined or not a string."; 123 | default: 124 | return "JSON error: unspecified error."; 125 | } 126 | } 127 | 128 | private: 129 | json_errc code_; 130 | }; 131 | 132 | namespace detail 133 | { 134 | enum class json_kind_t : unsigned char 135 | { 136 | undefined, 137 | null, 138 | boolean, 139 | number, 140 | integer, 141 | uinteger, 142 | string, 143 | array, 144 | object 145 | }; 146 | 147 | template 148 | struct json_traits 149 | { 150 | using variant_type = Variant; 151 | using allocator_type = Allocator; 152 | using node_type = Allocator::value_type; 153 | static_assert(std::variant_size_v == 9uz); 154 | static_assert(std::is_same_v>); 155 | static_assert(std::is_same_v>); 156 | static_assert(std::is_same_v>); 157 | using number_type = std::variant_alternative_t<3uz, variant_type>; 158 | static_assert(std::floating_point); 159 | using integer_type = std::variant_alternative_t<4uz, variant_type>; 160 | static_assert(std::signed_integral || std::is_same_v); 161 | using uinteger_type = std::variant_alternative_t<5uz, variant_type>; 162 | static_assert(std::unsigned_integral || std::is_same_v); 163 | using raw_string_type = std::variant_alternative_t<6uz, variant_type>; 164 | 165 | template 166 | using defancy_remove_pointer_t = std::remove_pointer_t()))>; 167 | 168 | template 169 | struct get_string_type 170 | { 171 | using type = RawStringT; 172 | }; 173 | 174 | template 175 | requires(!requires { typename RawStringT::value_type; }) 176 | struct get_string_type 177 | { 178 | using type = defancy_remove_pointer_t; 179 | }; 180 | 181 | using string_type = get_string_type::type; 182 | using char_type = string_type::value_type; 183 | static_assert(std::integral); 184 | static inline constexpr bool is_string_view = std::is_same_v; 185 | using raw_array_type = std::variant_alternative_t<7uz, variant_type>; 186 | using array_type = defancy_remove_pointer_t; 187 | using raw_object_type = std::variant_alternative_t<8uz, variant_type>; 188 | using object_type = defancy_remove_pointer_t; 189 | 190 | template 191 | using rebind_traits = std::allocator_traits::template rebind_traits; 192 | 193 | static_assert( 194 | !is_string_view && std::is_same_v::pointer, raw_string_type>); 195 | // unused for support winrt::hstring and Qstring 196 | // static_assert(is_string_view && std::is_trivially_copy_constructible_v 197 | // && std::is_trivially_copy_assignable_v && std::is_trivially_destructible_v); 198 | static_assert(std::is_same_v::pointer, raw_array_type>); 199 | static_assert(std::is_same_v::pointer, raw_object_type>); 200 | using key_string_type = object_type::key_type; 201 | using key_char_type = key_string_type::value_type; 202 | static_assert(std::integral); 203 | using map_node_type = object_type::node_type; 204 | 205 | template 206 | constexpr static void rebind_destroy_deallocate(allocator_type const& a, Tp ptr) noexcept 207 | { 208 | using T = std::remove_reference_t; 209 | using RA = std::allocator_traits::template rebind_alloc; 210 | 211 | auto ra = RA(a); 212 | std::allocator_traits::destroy(ra, ptr); 213 | std::allocator_traits::deallocate(ra, ptr, 1uz); 214 | } 215 | 216 | template 217 | constexpr static auto rebind_allocate_construct(allocator_type const& a, Args&&... args) 218 | { 219 | using RA = std::allocator_traits::template rebind_alloc; 220 | 221 | auto ra = RA(a); 222 | auto addr = rebind_traits::allocate(ra, 1uz); 223 | 224 | struct guard 225 | { 226 | decltype(addr)& ptr; 227 | RA& ra1; 228 | bool released; 229 | 230 | ~guard() 231 | { 232 | if (!released) 233 | std::allocator_traits::deallocate(ra1, ptr, 1uz); 234 | } 235 | }; 236 | 237 | guard g{ addr, ra, false }; 238 | rebind_traits::construct(ra, addr, std::forward(args)...); 239 | g.released = true; 240 | 241 | return addr; 242 | } 243 | 244 | constexpr static json_kind_t get_kind(variant_type const& stor) noexcept 245 | { 246 | return static_cast(stor.index()); 247 | } 248 | 249 | template 250 | constexpr static T& get_raw(variant_type& stor) noexcept 251 | { 252 | return *std::get_if(&stor); 253 | } 254 | 255 | template 256 | constexpr static const T& get_raw(variant_type const& stor) noexcept 257 | { 258 | return *std::get_if(&stor); 259 | } 260 | 261 | template 262 | constexpr static auto& get_val(V& stor) noexcept 263 | { 264 | if constexpr (std::is_same_v && is_string_view) 265 | return get_raw(stor); 266 | else if constexpr (std::is_same_v && !is_string_view) 267 | return *get_raw(stor); 268 | else if constexpr (std::is_same_v) 269 | return *get_raw(stor); 270 | else if constexpr (std::is_same_v) 271 | return *get_raw(stor); 272 | else 273 | return get_raw(stor); 274 | } 275 | 276 | constexpr static void set_undefined(variant_type& stor) { stor.template emplace(); } 277 | 278 | constexpr static void set_null(variant_type& stor) { stor.template emplace(); } 279 | 280 | constexpr static void set_boolean(variant_type& stor, bool value) noexcept 281 | { 282 | stor.template emplace(value); 283 | } 284 | 285 | constexpr static void set_number(variant_type& stor, number_type value) noexcept 286 | { 287 | stor.template emplace(value); 288 | } 289 | 290 | constexpr static void set_integer(variant_type& stor, integer_type value) noexcept 291 | { 292 | stor.template emplace(value); 293 | } 294 | 295 | constexpr static void set_uinteger(variant_type& stor, uinteger_type value) noexcept 296 | { 297 | stor.template emplace(value); 298 | } 299 | 300 | template 301 | constexpr static void set_string(variant_type& stor, allocator_type& alloc, Args&&... args) noexcept 302 | { 303 | if constexpr (is_string_view) 304 | stor.template emplace(std::forward(args)...); 305 | else 306 | stor.template emplace(rebind_allocate_construct( 307 | alloc, std::forward(std::forward(args)...))); 308 | } 309 | 310 | template 311 | constexpr static void set_array(variant_type& stor, allocator_type& alloc, Args&&... args) noexcept 312 | { 313 | stor.template emplace( 314 | rebind_allocate_construct(alloc, std::forward(args)...)); 315 | } 316 | 317 | template 318 | constexpr static void set_object(variant_type& stor, allocator_type& alloc, Args&&... args) noexcept 319 | { 320 | stor.template emplace( 321 | rebind_allocate_construct(alloc, std::forward(args)...)); 322 | } 323 | }; 324 | 325 | template 326 | concept transparently_comparable_associative = requires { 327 | typename Object::key_compare; 328 | typename Object::key_compare::is_transparent; 329 | }; 330 | 331 | template 332 | concept transparently_comparable_unordered_associative = requires { 333 | typename Object::key_equal; 334 | typename Object::key_equal::is_transparent; 335 | typename Object::hasher; 336 | typename Object::hasher::is_transparent; 337 | }; 338 | 339 | template 340 | concept transparently_comparable_json_object 341 | = transparently_comparable_associative || transparently_comparable_unordered_associative; 342 | 343 | template 344 | concept noncovertible_to_key_char_cptr 345 | = !std::is_convertible_v; 346 | 347 | template 348 | struct integer_base 349 | { 350 | static_assert(std::signed_integral && std::unsigned_integral); 351 | using integer_type = Integer; 352 | using uinteger_type = UInteger; 353 | }; 354 | 355 | template 356 | struct integer_base 357 | { 358 | using integer_type = Integer; 359 | }; 360 | 361 | template 362 | struct integer_base 363 | { 364 | using uinteger_type = UInteger; 365 | }; 366 | 367 | template <> 368 | struct integer_base 369 | { 370 | }; 371 | 372 | template 373 | class basic_json_slice_common_base 374 | : public detail::integer_base::integer_type, 375 | typename detail::json_traits::uinteger_type> 376 | { 377 | using json_traits_t = json_traits; 378 | using integer_type_internal = json_traits_t::integer_type; 379 | using uinteger_type_internal = json_traits_t::uinteger_type; 380 | 381 | public: 382 | using json_type = basic_json; 383 | 384 | friend Slice; 385 | friend json_type; 386 | 387 | using node_type = json_traits_t::node_type; 388 | using value_type = json_traits_t::node_type; 389 | using object_type = json_traits_t::object_type; 390 | using array_type = json_traits_t::array_type; 391 | using string_type = json_traits_t::string_type; 392 | using number_type = json_traits_t::number_type; 393 | using char_type = json_traits_t::char_type; 394 | using map_node_type = json_traits_t::map_node_type; 395 | using allocator_type = json_traits_t::allocator_type; 396 | using key_string_type = json_traits_t::key_string_type; 397 | using key_char_type = json_traits_t::key_char_type; 398 | 399 | static inline constexpr bool has_integer = !std::is_same_v; 400 | static inline constexpr bool has_uinteger = !std::is_same_v; 401 | 402 | node_type* node_{}; // made private in derived classes 403 | 404 | private: 405 | constexpr json_kind_t kind() const { return static_cast(*this).kind(); } 406 | 407 | template 408 | constexpr auto& get_val() 409 | { 410 | return static_cast(*this).template get_val(); 411 | } 412 | 413 | template 414 | constexpr auto& get_val() const 415 | { 416 | return static_cast(*this).template get_val(); 417 | } 418 | 419 | constexpr basic_json_slice_common_base(node_type* node) noexcept { node_ = node; }; 420 | 421 | public: 422 | constexpr basic_json_slice_common_base() noexcept = default; 423 | 424 | [[nodiscard]] constexpr bool empty() const noexcept { return node_ != nullptr; } 425 | 426 | [[nodiscard]] constexpr bool undefined() const noexcept { return kind() == json_kind_t::undefined; } 427 | 428 | [[nodiscard]] constexpr bool string() const noexcept { return kind() == json_kind_t::string; } 429 | 430 | [[nodiscard]] constexpr bool null() const noexcept { return kind() == json_kind_t::null; } 431 | 432 | [[nodiscard]] constexpr bool boolean() const noexcept { return kind() == json_kind_t::boolean; } 433 | 434 | [[nodiscard]] constexpr bool number() const noexcept 435 | { 436 | auto k = kind(); 437 | 438 | bool is_integer{}; 439 | bool is_uinteger{}; 440 | 441 | if constexpr (has_integer) 442 | { 443 | is_integer = k == json_kind_t::integer; 444 | } 445 | 446 | if constexpr (has_uinteger) 447 | { 448 | is_uinteger = k == json_kind_t::uinteger; 449 | } 450 | 451 | return k == json_kind_t::number || is_integer || is_uinteger; 452 | } 453 | 454 | [[nodiscard]] constexpr bool object() const noexcept { return kind() == json_kind_t::object; } 455 | 456 | [[nodiscard]] constexpr bool array() const noexcept { return kind() == json_kind_t::array; } 457 | 458 | [[nodiscard]] constexpr bool integer() const noexcept 459 | requires has_integer 460 | { 461 | return kind() == json_kind_t::integer; 462 | } 463 | 464 | [[nodiscard]] constexpr bool uinteger() const noexcept 465 | requires has_uinteger 466 | { 467 | return kind() == json_kind_t::uinteger; 468 | } 469 | 470 | constexpr explicit operator bool() const 471 | { 472 | auto k = kind(); 473 | 474 | if (!k == json_kind_t::boolean) 475 | throw json_error(json_errc::not_boolean); 476 | 477 | return get_val(); 478 | } 479 | 480 | constexpr explicit operator number_type() const 481 | { 482 | auto k = kind(); 483 | 484 | if constexpr (has_integer && has_uinteger) 485 | { 486 | if (k == json_kind_t::number) 487 | return get_val(); 488 | else if (k == json_kind_t::integer) 489 | return get_val(); 490 | else if (k == json_kind_t::uinteger) 491 | return get_val(); 492 | } 493 | else if constexpr (has_integer) 494 | { 495 | if (k == json_kind_t::number) 496 | return get_val(); 497 | else if (k == json_kind_t::integer) 498 | return get_val(); 499 | } 500 | else if (has_uinteger) 501 | { 502 | if (k == json_kind_t::number) 503 | return get_val(); 504 | else if (k == json_kind_t::uinteger) 505 | return get_val(); 506 | } 507 | 508 | throw json_error(json_errc::not_number); 509 | } 510 | 511 | constexpr explicit operator nulljson_t() const 512 | { 513 | if (!null()) 514 | throw json_error(json_errc::not_null); 515 | 516 | return nulljson; 517 | } 518 | 519 | constexpr explicit operator string_type const&() const& 520 | { 521 | if (!string()) 522 | throw json_error(json_errc::not_string); 523 | 524 | return get_val(); 525 | } 526 | 527 | constexpr explicit operator array_type const&() const& 528 | { 529 | if (!array()) 530 | throw json_error(json_errc::not_array); 531 | 532 | return get_val(); 533 | } 534 | 535 | constexpr explicit operator object_type const&() const& 536 | { 537 | if (!object()) 538 | throw json_error(json_errc::not_object); 539 | 540 | return get_val(); 541 | } 542 | 543 | constexpr explicit operator integer_type_internal() const 544 | requires has_integer 545 | { 546 | if (!integer()) 547 | throw json_error(json_errc::not_integer); 548 | 549 | return get_val(); 550 | } 551 | 552 | constexpr explicit operator uinteger_type_internal() const 553 | requires has_uinteger 554 | { 555 | if (!uinteger()) 556 | throw json_error(json_errc::not_uinteger); 557 | 558 | return get_val(); 559 | } 560 | }; 561 | } 562 | 563 | template 564 | class basic_const_json_slice: public detail::basic_json_slice_common_base, 565 | decltype(Node::stor), decltype(Node::alloc)> 566 | { 567 | using json_traits_t = detail::json_traits; 568 | using base_type = detail::basic_json_slice_common_base, decltype(Node::stor), 569 | decltype(Node::alloc)>; 570 | 571 | friend base_type; 572 | 573 | // defined in base_type, but base_type can't access its members, 574 | // so use the CRTP derived class to provide the actual accessor 575 | using base_type::node_; 576 | 577 | constexpr detail::json_kind_t kind() const { return json_traits_t::get_kind(node_->stor); } 578 | 579 | template 580 | constexpr auto& get_val() 581 | { 582 | return json_traits_t::template get_val(node_->stor); 583 | } 584 | 585 | template 586 | constexpr auto& get_val() const 587 | { 588 | return json_traits_t::template get_val(node_->stor); 589 | } 590 | 591 | public: 592 | using base_type::array; 593 | using base_type::boolean; 594 | using base_type::integer; 595 | using base_type::null; 596 | using base_type::number; 597 | using base_type::object; 598 | using base_type::string; 599 | using base_type::uinteger; 600 | using base_type::undefined; 601 | 602 | using base_type::has_integer; 603 | using base_type::has_uinteger; 604 | 605 | using typename base_type::allocator_type; 606 | using typename base_type::array_type; 607 | using typename base_type::char_type; 608 | using typename base_type::json_type; 609 | using typename base_type::key_char_type; 610 | using typename base_type::key_string_type; 611 | using typename base_type::map_node_type; 612 | using typename base_type::node_type; 613 | using typename base_type::number_type; 614 | using typename base_type::object_type; 615 | using typename base_type::string_type; 616 | using typename base_type::value_type; 617 | 618 | constexpr void swap(basic_const_json_slice& rhs) noexcept 619 | { 620 | auto temp = node_; 621 | node_ = rhs.node_; 622 | rhs.node_ = temp; 623 | } 624 | 625 | friend constexpr void swap(basic_const_json_slice& lhs, basic_const_json_slice& rhs) noexcept { lhs.swap(rhs); } 626 | 627 | // Similar to iterators, default construction is allowed, but except for operator=, 628 | // operations on default-constructed slice cause undefined behavior. 629 | // The default constructor is intentionally provided for default arguments. 630 | constexpr basic_const_json_slice() noexcept = default; 631 | 632 | constexpr basic_const_json_slice(basic_const_json_slice&&) noexcept = default; 633 | 634 | constexpr basic_const_json_slice(basic_const_json_slice const&) noexcept = default; 635 | 636 | constexpr basic_const_json_slice(json_type const& j) noexcept 637 | : base_type{ const_cast(std::addressof(j.node_)) } 638 | { 639 | } 640 | 641 | constexpr basic_const_json_slice(node_type const& n) noexcept 642 | : base_type{ const_cast(std::addressof(n)) } 643 | { 644 | } 645 | 646 | constexpr basic_const_json_slice(basic_json_slice const& s) noexcept 647 | : base_type{ s.node_ } 648 | { 649 | } 650 | 651 | constexpr basic_const_json_slice& operator=(basic_const_json_slice const&) noexcept = default; 652 | 653 | constexpr basic_const_json_slice& operator=(basic_const_json_slice&&) noexcept = default; 654 | 655 | template 656 | requires detail::transparently_comparable_json_object 657 | && detail::noncovertible_to_key_char_cptr 658 | constexpr auto operator[](KeyStrLike const& k) const -> basic_const_json_slice 659 | { 660 | if (!object()) 661 | throw json_error(json_errc::nonobject_indexing); 662 | 663 | auto const& o = get_val(); 664 | auto i = o.find(k); 665 | 666 | if (i == o.end()) 667 | throw json_error(json_errc::key_not_found); 668 | 669 | auto& [_, v] = *i; 670 | 671 | return v; 672 | } 673 | 674 | constexpr auto operator[](key_char_type const* k) const -> basic_const_json_slice 675 | { 676 | if (!object()) 677 | throw json_error(json_errc::nonobject_indexing); 678 | 679 | auto const& o = get_val(); 680 | auto i = o.find(k); 681 | 682 | if (i == o.end()) 683 | throw json_error(json_errc::key_not_found); 684 | 685 | auto& [_, v] = *i; 686 | 687 | return v; 688 | } 689 | 690 | template 691 | constexpr auto operator[](T pos) const -> basic_const_json_slice 692 | { 693 | if (!array()) 694 | throw json_error(json_errc::nonarray_indexing); 695 | 696 | auto const& a = get_val(); 697 | 698 | return a[pos]; 699 | } 700 | 701 | constexpr auto as_array() const 702 | { 703 | constexpr auto node_to_slice 704 | = [](node_type const& node) static noexcept { return basic_const_json_slice{ node }; }; 705 | 706 | return static_cast(*this) | std::views::transform(node_to_slice); 707 | } 708 | 709 | constexpr auto as_object() const 710 | { 711 | constexpr auto pair_node_to_slice = [](object_type::value_type const& pair) static noexcept { 712 | auto& [key, value]{ pair }; 713 | return std::pair{ key, value }; 714 | }; 715 | 716 | return static_cast(*this) | std::views::transform(pair_node_to_slice); 717 | } 718 | 719 | constexpr auto operator[](key_string_type const& k) const -> basic_const_json_slice 720 | { 721 | if (!object()) 722 | throw json_error(json_errc::nonobject_indexing); 723 | 724 | auto const& o = get_val(); 725 | auto i = o.find(k); 726 | 727 | if (i == o.end()) 728 | throw json_error(json_errc::key_not_found); 729 | 730 | auto& [_, v] = *i; 731 | 732 | return v; 733 | } 734 | }; 735 | 736 | template 737 | class basic_json_slice: public detail::basic_json_slice_common_base, decltype(Node::stor), 738 | decltype(Node::alloc)> 739 | { 740 | using json_traits_t = detail::json_traits; 741 | using base_type 742 | = detail::basic_json_slice_common_base, decltype(Node::stor), decltype(Node::alloc)>; 743 | 744 | friend base_type; 745 | friend basic_const_json_slice; 746 | 747 | // defined in base_type, but base_type can't access its members, 748 | // so use the CRTP derived class to provide the actual accessor 749 | using base_type::node_; 750 | 751 | constexpr detail::json_kind_t kind() const { return json_traits_t::get_kind(node_->stor); } 752 | 753 | template 754 | constexpr auto& get_val() 755 | { 756 | return json_traits_t::template get_val(node_->stor); 757 | } 758 | 759 | template 760 | constexpr auto& get_val() const 761 | { 762 | return json_traits_t::template get_val(node_->stor); 763 | } 764 | 765 | public: 766 | using base_type::array; 767 | using base_type::boolean; 768 | using base_type::integer; 769 | using base_type::null; 770 | using base_type::number; 771 | using base_type::object; 772 | using base_type::string; 773 | using base_type::uinteger; 774 | using base_type::undefined; 775 | 776 | using base_type::has_integer; 777 | using base_type::has_uinteger; 778 | 779 | using typename base_type::allocator_type; 780 | using typename base_type::array_type; 781 | using typename base_type::char_type; 782 | using typename base_type::json_type; 783 | using typename base_type::key_char_type; 784 | using typename base_type::key_string_type; 785 | using typename base_type::map_node_type; 786 | using typename base_type::node_type; 787 | using typename base_type::number_type; 788 | using typename base_type::object_type; 789 | using typename base_type::string_type; 790 | using typename base_type::value_type; 791 | 792 | constexpr void swap(basic_json_slice& rhs) noexcept 793 | { 794 | auto temp = node_; 795 | node_ = rhs.node_; 796 | rhs.node_ = temp; 797 | } 798 | 799 | friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept { lhs.swap(rhs); } 800 | 801 | // Similar to iterators, default construction is allowed, but except for operator=, 802 | // operations on default-constructed slice cause undefined behavior. 803 | // The default constructor is intentionally provided for default arguments. 804 | constexpr basic_json_slice() noexcept = default; 805 | 806 | constexpr basic_json_slice(basic_json_slice&&) noexcept = default; 807 | 808 | constexpr basic_json_slice(basic_json_slice const&) noexcept = default; 809 | 810 | constexpr basic_json_slice(json_type& j) noexcept 811 | : base_type{ std::addressof(j.node_) } 812 | { 813 | } 814 | 815 | constexpr basic_json_slice(node_type& n) noexcept 816 | : base_type{ std::addressof(n) } 817 | { 818 | } 819 | 820 | constexpr basic_json_slice& operator=(basic_json_slice const&) noexcept = default; 821 | 822 | constexpr basic_json_slice& operator=(basic_json_slice&&) noexcept = default; 823 | 824 | constexpr explicit operator string_type&() & 825 | { 826 | if (!string()) 827 | throw json_error(json_errc::not_string); 828 | 829 | return base_type::template get_val(); 830 | } 831 | 832 | constexpr explicit operator array_type&() & 833 | { 834 | if (!array()) 835 | throw json_error(json_errc::not_array); 836 | 837 | return base_type::template get_val(); 838 | } 839 | 840 | constexpr explicit operator object_type&() & 841 | { 842 | if (!object()) 843 | throw json_error(json_errc::not_object); 844 | 845 | return base_type::template get_val(); 846 | } 847 | 848 | constexpr void reset() noexcept 849 | { 850 | assert(node_); 851 | json_type::clear_variant(node_->stor, node_->alloc, kind()); 852 | json_traits_t::set_undefined(node_->stor); 853 | } 854 | 855 | constexpr basic_json_slice operator[](key_string_type const& k) 856 | { 857 | auto kd = kind(); 858 | using enum detail::json_kind_t; 859 | 860 | if (!(kd == object || kd == undefined)) 861 | throw json_error(json_errc::nonobject_indexing); 862 | 863 | json_traits_t::set_object(node_->stor, node_->alloc); 864 | 865 | auto& o = base_type::template get_val(); 866 | auto [i, _] = o.emplace(k, node_type{ node_->alloc }); 867 | auto& [unused, v] = *i; 868 | 869 | return v; 870 | } 871 | 872 | template 873 | requires detail::transparently_comparable_json_object 874 | && detail::noncovertible_to_key_char_cptr 875 | constexpr basic_json_slice operator[](KeyStrLike const& k) 876 | { 877 | auto kd = kind(); 878 | using enum detail::json_kind_t; 879 | 880 | if (!(kd == object || kd == undefined)) 881 | throw json_error(json_errc::nonobject_indexing); 882 | 883 | json_traits_t::set_object(node_->stor, node_->alloc); 884 | 885 | auto& o = base_type::template get_val(); 886 | auto [i, _] = o.emplace(k, node_type{ node_->alloc }); 887 | auto& [unused, v] = *i; 888 | 889 | return v; 890 | } 891 | 892 | constexpr basic_json_slice operator[](key_char_type const* k) 893 | { 894 | auto kd = kind(); 895 | using enum detail::json_kind_t; 896 | 897 | if (!(kd == object || kd == undefined)) 898 | throw json_error(json_errc::nonobject_indexing); 899 | 900 | json_traits_t::set_object(node_->stor, node_->alloc); 901 | 902 | auto& o = base_type::template get_val(); 903 | auto [i, _] = o.emplace(k, node_type{ node_->alloc }); 904 | auto& [unused, v] = *i; 905 | 906 | return v; 907 | } 908 | 909 | template 910 | constexpr basic_json_slice operator[](T pos) 911 | { 912 | if (!array()) 913 | throw json_error(json_errc::nonarray_indexing); 914 | 915 | auto& a = base_type::template get_val(); 916 | 917 | return a[pos]; 918 | } 919 | 920 | constexpr basic_json_slice& operator=(string_type const& str) 921 | { 922 | auto kd = kind(); 923 | using enum detail::json_kind_t; 924 | 925 | if (!(kd == string || kd == undefined)) 926 | throw json_error(json_errc::not_undefined_or_string); 927 | 928 | if (kd == string) 929 | { 930 | base_type::template get_val() = str; 931 | } 932 | else // undefined 933 | { 934 | json_traits_t::set_string(node_->stor, node_->alloc, str); 935 | } 936 | 937 | return *this; 938 | } 939 | 940 | constexpr basic_json_slice& operator=(string_type&& str) 941 | { 942 | auto kd = kind(); 943 | using enum detail::json_kind_t; 944 | 945 | if (!(kd == string || kd == undefined)) 946 | throw json_error(json_errc::not_undefined_or_string); 947 | 948 | if (kd == string) 949 | { 950 | base_type::template get_val() = std::move(str); 951 | } 952 | else // undefined 953 | { 954 | json_traits_t::set_string(node_->stor, node_->alloc, str); 955 | } 956 | 957 | return *this; 958 | } 959 | 960 | constexpr basic_json_slice& operator=(char_type const* str) 961 | { 962 | auto kd = kind(); 963 | using enum detail::json_kind_t; 964 | 965 | if (!(kd == string || kd == undefined)) 966 | throw json_error(json_errc::not_undefined_or_string); 967 | 968 | if (kd == string) 969 | { 970 | base_type::template get_val() = str; 971 | } 972 | else // undefined 973 | { 974 | json_traits_t::set_string(node_->stor, node_->alloc, str); 975 | } 976 | 977 | return *this; 978 | } 979 | 980 | template 981 | requires std::constructible_from 982 | && (!std::is_convertible_v) 983 | constexpr basic_json_slice& operator=(StrLike const& str) 984 | { 985 | auto kd = kind(); 986 | using enum detail::json_kind_t; 987 | 988 | if (!(kd == string || kd == undefined)) 989 | throw json_error(json_errc::not_undefined_or_string); 990 | 991 | if (kd == string) 992 | { 993 | base_type::template get_val() = str; 994 | } 995 | else // undefined 996 | { 997 | json_traits_t::set_string(node_->stor, node_->alloc, str); 998 | } 999 | 1000 | return *this; 1001 | } 1002 | 1003 | constexpr basic_json_slice& operator=(nulljson_t) 1004 | { 1005 | auto kd = kind(); 1006 | using enum detail::json_kind_t; 1007 | 1008 | if (!(kd == null || kd == undefined)) 1009 | throw json_error(json_errc::not_undefined_or_null); 1010 | 1011 | json_traits_t::set_null(node_->stor); 1012 | 1013 | return *this; 1014 | } 1015 | 1016 | template 1017 | requires std::is_arithmetic_v 1018 | constexpr basic_json_slice& operator=(T n) 1019 | { 1020 | if constexpr (std::same_as) 1021 | { 1022 | auto kd = kind(); 1023 | using enum detail::json_kind_t; 1024 | 1025 | if (!(kd == boolean || kd == undefined)) 1026 | throw json_error(json_errc::not_undefined_or_boolean); 1027 | 1028 | json_traits_t::set_boolean(node_->stor, n); 1029 | } 1030 | else if constexpr (has_integer && std::signed_integral) 1031 | { 1032 | auto kd = kind(); 1033 | using enum detail::json_kind_t; 1034 | 1035 | if (!(kd == integer || kd == undefined)) 1036 | throw json_error(json_errc::not_undefined_or_number); 1037 | 1038 | json_traits_t::set_integer(node_->stor, n); 1039 | } 1040 | else if constexpr (has_uinteger && std::unsigned_integral) 1041 | { 1042 | auto kd = kind(); 1043 | using enum detail::json_kind_t; 1044 | 1045 | if (!(kd == uinteger || kd == undefined)) 1046 | throw json_error(json_errc::not_undefined_or_number); 1047 | 1048 | json_traits_t::set_uinteger(node_->stor, n); 1049 | } 1050 | else // fallback 1051 | { 1052 | auto kd = kind(); 1053 | using enum detail::json_kind_t; 1054 | 1055 | if (!(kd == number || kd == undefined)) 1056 | throw json_error(json_errc::not_undefined_or_number); 1057 | 1058 | json_traits_t::set_number(node_->stor, n); 1059 | } 1060 | 1061 | return *this; 1062 | } 1063 | 1064 | constexpr basic_json_slice& operator=(json_type& j) noexcept 1065 | { 1066 | node_ = std::addressof(j.node_); 1067 | 1068 | return *this; 1069 | } 1070 | 1071 | constexpr basic_json_slice& operator=(node_type& n) noexcept 1072 | { 1073 | node_ = std::addressof(n); 1074 | 1075 | return *this; 1076 | } 1077 | 1078 | constexpr auto as_array() 1079 | { 1080 | constexpr auto node_to_slice = [](node_type& node) static noexcept { return basic_json_slice{ node }; }; 1081 | 1082 | return static_cast(*this) | std::views::transform(node_to_slice); 1083 | } 1084 | 1085 | constexpr auto as_object() 1086 | { 1087 | constexpr auto pair_node_to_slice = [](object_type::value_type& pair) static noexcept { 1088 | auto& [key, value]{ pair }; 1089 | return std::pair{ key, value }; 1090 | }; 1091 | 1092 | return static_cast(*this) | std::views::transform(pair_node_to_slice); 1093 | } 1094 | }; 1095 | 1096 | template 1097 | class basic_json: public detail::integer_base< 1098 | typename detail::json_traits::integer_type, 1099 | typename detail::json_traits::uinteger_type> 1100 | { 1101 | using json_traits_t = detail::json_traits; 1102 | 1103 | // LWG3917 https://cplusplus.github.io/LWG/issue3917 1104 | // The standard is not clear on whether allocators support incomplete types, 1105 | // but all current active implementations support this. 1106 | static_assert(std::is_same_v); 1107 | 1108 | public: 1109 | using allocator_type = json_traits_t::allocator_type; 1110 | using node_type = Node; 1111 | using value_type = Node; 1112 | using number_type = json_traits_t::number_type; 1113 | using object_type = json_traits_t::object_type; 1114 | using array_type = json_traits_t::array_type; 1115 | using string_type = json_traits_t::string_type; 1116 | using char_type = string_type::value_type; 1117 | using map_node_type = object_type::value_type; 1118 | using key_string_type = object_type::key_type; 1119 | using key_char_type = key_string_type::value_type; 1120 | 1121 | using slice_type = basic_json_slice; 1122 | using const_slice_type = basic_const_json_slice; 1123 | 1124 | friend const_slice_type; 1125 | friend slice_type; 1126 | 1127 | static inline constexpr bool has_integer 1128 | = !std::is_same_v; 1129 | static inline constexpr bool has_uinteger 1130 | = !std::is_same_v; 1131 | 1132 | private: 1133 | static constexpr bool is_ator_stateless_ = std::allocator_traits::is_always_equal::value; 1134 | static constexpr bool is_pocca_ 1135 | = std::allocator_traits::propagate_on_container_copy_assignment::value; 1136 | static constexpr bool is_pocma_ 1137 | = std::allocator_traits::propagate_on_container_move_assignment::value; 1138 | static constexpr bool is_pocs_ = std::allocator_traits::propagate_on_container_swap::value; 1139 | 1140 | static_assert(std::integral); 1141 | static_assert(std::integral); 1142 | static_assert(std::floating_point); 1143 | static_assert(std::same_as); 1144 | static_assert(std::same_as); 1145 | static_assert(std::random_access_iterator); 1146 | static_assert(std::random_access_iterator); 1147 | static_assert(std::bidirectional_iterator); 1148 | static_assert(std::random_access_iterator); 1149 | 1150 | node_type node_; 1151 | 1152 | constexpr detail::json_kind_t kind() const noexcept { return json_traits_t::get_kind(node_.stor); } 1153 | 1154 | constexpr void reset() noexcept 1155 | { 1156 | clear_variant(node_.stor, node_.alloc, kind()); 1157 | json_traits_t::set_undefined(node_.stor); 1158 | } 1159 | 1160 | struct rollbacker_array_part_ 1161 | { 1162 | array_type::iterator& sentry; 1163 | array_type& array; 1164 | 1165 | constexpr rollbacker_array_part_() noexcept = delete; 1166 | 1167 | constexpr rollbacker_array_part_(rollbacker_array_part_ const&) noexcept = delete; 1168 | 1169 | constexpr rollbacker_array_part_(array_type::iterator& i, array_type& a) noexcept 1170 | : sentry(i) 1171 | , array(a) 1172 | { 1173 | } 1174 | 1175 | constexpr ~rollbacker_array_part_() 1176 | { 1177 | auto end = array.end(); 1178 | 1179 | if (sentry == end) 1180 | return; 1181 | 1182 | for (auto begin = array.begin(); begin != sentry; ++begin) 1183 | { 1184 | clear_variant(begin->stor, begin->alloc, json_traits_t::get_kind(begin->stor)); 1185 | } 1186 | } 1187 | }; 1188 | 1189 | struct rollbacker_array_all_ 1190 | { 1191 | array_type& array; 1192 | bool done; 1193 | 1194 | constexpr rollbacker_array_all_() noexcept = delete; 1195 | 1196 | constexpr rollbacker_array_all_(rollbacker_array_all_ const&) noexcept = delete; 1197 | 1198 | constexpr void release() noexcept { done = true; } 1199 | 1200 | constexpr rollbacker_array_all_(array_type& a) noexcept 1201 | : array(a) 1202 | , done(false) 1203 | { 1204 | } 1205 | 1206 | constexpr ~rollbacker_array_all_() 1207 | { 1208 | if (done) 1209 | return; 1210 | 1211 | auto end = array.end(); 1212 | 1213 | for (auto begin = array.begin(); begin != end; ++begin) 1214 | { 1215 | clear_variant(begin->stor, begin->alloc, json_traits_t::get_kind(begin->stor)); 1216 | } 1217 | } 1218 | }; 1219 | 1220 | struct rollbacker_map_all_ 1221 | { 1222 | object_type& object; 1223 | bool done; 1224 | 1225 | constexpr void release() noexcept { done = true; } 1226 | 1227 | constexpr rollbacker_map_all_(object_type& o) noexcept 1228 | : object(o) 1229 | , done(false) 1230 | { 1231 | } 1232 | 1233 | constexpr ~rollbacker_map_all_() 1234 | { 1235 | if (done) 1236 | return; 1237 | 1238 | auto end = object.end(); 1239 | auto begin = object.begin(); 1240 | 1241 | for (auto begin = object.begin(); begin != end; ++begin) 1242 | { 1243 | auto&& [key, value] = *begin; 1244 | clear_variant(value.stor, value.alloc, json_traits_t::get_kind(value.stor)); 1245 | } 1246 | } 1247 | }; 1248 | 1249 | constexpr void clone(const basic_json& rhs) { copy_variant(rhs.node_.stor, node_.stor, node_.alloc); } 1250 | 1251 | public: 1252 | constexpr void swap(basic_json& rhs) noexcept // strengthened 1253 | { 1254 | std::ranges::swap(rhs.node_.stor, node_.stor); 1255 | if constexpr (!is_ator_stateless_) 1256 | { 1257 | if constexpr (is_pocs_) 1258 | { 1259 | using std::swap; 1260 | swap(node_.alloc, rhs.node_.alloc); // ADL-swap 1261 | } 1262 | else 1263 | { 1264 | // UB if unequal 1265 | assert(node_.alloc == rhs.node_.alloc); 1266 | } 1267 | } 1268 | } 1269 | 1270 | friend constexpr void swap(basic_json& lhs, basic_json& rhs) noexcept // strengthened 1271 | { 1272 | lhs.swap(rhs); 1273 | } 1274 | 1275 | // clang-format off 1276 | constexpr basic_json() requires std::default_initializable = default; 1277 | // clang-format on 1278 | 1279 | constexpr explicit basic_json(allocator_type const& a) noexcept 1280 | : node_(a) 1281 | { 1282 | } 1283 | 1284 | constexpr basic_json(basic_json&& rhs) noexcept 1285 | : node_(rhs.node_.alloc) 1286 | { 1287 | std::ranges::swap(rhs.node_.stor, node_.stor); 1288 | } 1289 | constexpr basic_json(basic_json&& rhs, allocator_type const& a) noexcept(is_ator_stateless_) 1290 | : node_(a) 1291 | { 1292 | if constexpr (is_ator_stateless_) 1293 | { 1294 | std::ranges::swap(rhs.node_.stor, node_.stor); 1295 | } 1296 | else 1297 | { 1298 | if (a == rhs.node_.alloc) 1299 | { 1300 | std::ranges::swap(rhs.node_.stor, node_.stor); 1301 | } 1302 | else 1303 | { 1304 | clone(rhs); 1305 | rhs.reset(); 1306 | } 1307 | } 1308 | } 1309 | 1310 | constexpr basic_json(basic_json const& rhs) 1311 | : node_(std::allocator_traits::select_on_container_copy_construction(rhs.node_.alloc)) 1312 | { 1313 | clone(rhs); 1314 | } 1315 | constexpr explicit basic_json(basic_json const& rhs, allocator_type const& a) 1316 | : node_(a) 1317 | { 1318 | clone(rhs); 1319 | } 1320 | 1321 | constexpr basic_json& operator=(basic_json const& rhs) 1322 | { 1323 | if (this != std::addressof(rhs)) 1324 | { 1325 | reset(); 1326 | if constexpr (!is_ator_stateless_ && is_pocca_) 1327 | { 1328 | node_.alloc = rhs.node_.alloc; 1329 | } 1330 | clone(rhs); 1331 | } 1332 | 1333 | return *this; 1334 | } 1335 | 1336 | constexpr basic_json& operator=(basic_json&& rhs) noexcept(is_ator_stateless_ || is_pocma_) 1337 | { 1338 | if constexpr (is_ator_stateless_) 1339 | { 1340 | std::ranges::swap(rhs.node_.stor, node_.stor); 1341 | } 1342 | else if constexpr (is_pocma_) 1343 | { 1344 | std::ranges::swap(rhs.node_.stor, node_.stor); 1345 | // N.B. ADL-swap may be ill-formed or have undesired effect 1346 | std::ranges::swap(rhs.node_.alloc, node_.alloc); 1347 | } 1348 | else 1349 | { 1350 | if (node_.alloc == rhs.node_.alloc) 1351 | { 1352 | std::ranges::swap(rhs.node_.stor, node_.stor); 1353 | } 1354 | else 1355 | { 1356 | reset(); 1357 | clone(rhs); 1358 | rhs.reset(); 1359 | } 1360 | } 1361 | 1362 | return *this; 1363 | } 1364 | 1365 | constexpr basic_json(std::nullptr_t, allocator_type const& = allocator_type()) noexcept 1366 | = delete; // prevent implicit constructing string 1367 | 1368 | constexpr basic_json(nulljson_t, allocator_type const& a = allocator_type()) noexcept 1369 | : node_(a) 1370 | { 1371 | } 1372 | 1373 | template 1374 | requires std::is_arithmetic_v 1375 | constexpr basic_json(T n, allocator_type const& a = allocator_type()) noexcept 1376 | : node_(a) 1377 | { 1378 | if constexpr (std::same_as) 1379 | { 1380 | json_traits_t::set_boolean(node_.stor, n); 1381 | } 1382 | else if constexpr (has_integer && std::signed_integral) 1383 | { 1384 | json_traits_t::set_integer(node_.stor, n); 1385 | } 1386 | else if constexpr (has_uinteger && std::unsigned_integral) 1387 | { 1388 | json_traits_t::set_uinteger(node_.stor, n); 1389 | } 1390 | else // fallback 1391 | { 1392 | json_traits_t::set_number(node_.stor, n); 1393 | } 1394 | } 1395 | 1396 | constexpr explicit basic_json(string_type v, allocator_type const& a = allocator_type()) 1397 | : node_(a) 1398 | { 1399 | } 1400 | 1401 | constexpr basic_json( 1402 | char_type const* str, string_type::size_type count, allocator_type const& a = allocator_type()) 1403 | : node_(a) 1404 | { 1405 | json_traits_t::set_string(node_.stor, node_.alloc, str, count); 1406 | } 1407 | 1408 | constexpr explicit basic_json(char_type const* str, allocator_type const& a = allocator_type()) 1409 | : node_(a) 1410 | { 1411 | json_traits_t::set_string(node_.stor, node_.alloc, str); 1412 | } 1413 | 1414 | template 1415 | requires std::constructible_from 1416 | && (std::is_convertible_v == false) 1417 | constexpr basic_json(StrLike const& str, allocator_type const& a = allocator_type()) 1418 | : node_(a) 1419 | { 1420 | json_traits_t::set_string(node_.stor, node_.alloc, str); 1421 | } 1422 | 1423 | constexpr basic_json(array_type arr, allocator_type const& a = allocator_type()) 1424 | : node_(a) 1425 | { 1426 | rollbacker_array_all_ rollbacker(arr); 1427 | json_traits_t::set_array(node_.stor, node_.alloc, std::move(arr)); 1428 | rollbacker.release(); 1429 | } 1430 | 1431 | constexpr basic_json(object_type obj, allocator_type const& a = allocator_type()) 1432 | : node_(a) 1433 | { 1434 | rollbacker_map_all_ rollbacker(obj); 1435 | json_traits_t::set_object(node_.stor, node_.alloc, std::move(obj)); 1436 | rollbacker.release(); 1437 | } 1438 | 1439 | constexpr basic_json(node_type&& n) noexcept 1440 | : node_(std::move(n)) 1441 | { 1442 | json_traits_t::set_undefined(n.stor); 1443 | } 1444 | 1445 | constexpr basic_json(node_type&& n, allocator_type const& a) noexcept(is_ator_stateless_) 1446 | : node_(a) 1447 | { 1448 | if constexpr (is_ator_stateless_) 1449 | { 1450 | node_.stor = std::move(n.stor); 1451 | } 1452 | else 1453 | { 1454 | if (n.alloc == a) 1455 | { 1456 | node_.stor = std::move(n.stor); 1457 | } 1458 | else 1459 | { 1460 | copy_variant(n.stor, node_.stor, node_.alloc); 1461 | clear_variant(n.stor, n.alloc, json_traits_t::get_kind(n.stor)); 1462 | } 1463 | } 1464 | json_traits_t::set_undefined(n.stor); 1465 | } 1466 | 1467 | constexpr allocator_type get_allocator() const noexcept { return node_.alloc; } 1468 | 1469 | [[nodiscard("discard nodes will cause leaks")]] constexpr operator node_type() && noexcept 1470 | { 1471 | auto node = std::move(node_); 1472 | json_traits_t::set_undefined(node_.stor); 1473 | return node; 1474 | } 1475 | 1476 | public: 1477 | constexpr ~basic_json() noexcept { reset(); } 1478 | 1479 | constexpr slice_type slice() noexcept { return *this; } 1480 | 1481 | constexpr const_slice_type slice() const noexcept { return *this; } 1482 | 1483 | /* to maintain consistency with object, this implementation is not used 1484 | template 1485 | requires(std::constructible_from && ...) 1486 | static constexpr basic_json array(Args&&... args) 1487 | { 1488 | array_type arr; 1489 | arr.reserve(sizeof...(Args)); 1490 | (arr.push_back(basic_json(std::forward(args))), ...); 1491 | 1492 | return arr; 1493 | } 1494 | */ 1495 | private: 1496 | struct pair 1497 | { 1498 | key_string_type key; 1499 | basic_json value; 1500 | }; 1501 | 1502 | template 1503 | struct type_list 1504 | { 1505 | }; 1506 | 1507 | template 1508 | static consteval std::size_t get_pair_num_pair(type_list) noexcept 1509 | { 1510 | return 1 + get_pair_num(type_list{}); 1511 | } 1512 | 1513 | template 1514 | static consteval std::size_t get_pair_num(type_list) noexcept 1515 | { 1516 | if constexpr (std::same_as>) 1517 | { 1518 | return 1 + get_pair_num(type_list{}); 1519 | } 1520 | else 1521 | { 1522 | static_assert(sizeof...(Ts) > 0, "there should be a json value after string in json object"); 1523 | return get_pair_num_pair(type_list{}); 1524 | } 1525 | } 1526 | 1527 | static consteval std::size_t get_pair_num(type_list<>) noexcept { return 0; } 1528 | 1529 | public: 1530 | template 1531 | struct object 1532 | { 1533 | pair arr[N]; 1534 | 1535 | constexpr operator basic_json() && 1536 | { 1537 | object_type obj; 1538 | for (auto& i : arr) 1539 | { 1540 | obj.emplace(std::move(i.key), std::move(i.value)); 1541 | } 1542 | 1543 | return obj; 1544 | } 1545 | }; 1546 | 1547 | // deduction guide for object{...} 1548 | template 1549 | object(Ts&&...) -> object{})>; 1550 | 1551 | template 1552 | struct array 1553 | { 1554 | basic_json arr[N]; 1555 | 1556 | constexpr operator basic_json() && 1557 | { 1558 | array_type arr_; 1559 | for (auto& value : arr) 1560 | { 1561 | arr_.reserve(N); 1562 | arr_.push_back(std::move(value)); 1563 | } 1564 | 1565 | return arr_; 1566 | } 1567 | }; 1568 | 1569 | // deduction guide for array{...} 1570 | template 1571 | array(Ts&&...) -> array; 1572 | 1573 | private: 1574 | static void clear_variant( 1575 | decltype(Node::stor)& stor, allocator_type& alloc, detail::json_kind_t old_kind) noexcept 1576 | { 1577 | using enum detail::json_kind_t; 1578 | 1579 | switch (old_kind) 1580 | { 1581 | case string: 1582 | if constexpr (!json_traits_t::is_string_view) 1583 | { 1584 | auto ptr = json_traits_t::template get_raw(stor); 1585 | json_traits_t::rebind_destroy_deallocate(alloc, ptr); 1586 | } 1587 | case array: { 1588 | auto ptr = json_traits_t::template get_raw(stor); 1589 | for (auto& i : *ptr) 1590 | { 1591 | clear_variant(i.stor, i.alloc, json_traits_t::get_kind(i.stor)); 1592 | } 1593 | json_traits_t::rebind_destroy_deallocate(alloc, ptr); 1594 | } 1595 | case object: { 1596 | auto ptr = json_traits_t::template get_raw(stor); 1597 | for (auto& [key, value] : *ptr) 1598 | { 1599 | clear_variant(value.stor, value.alloc, json_traits_t::get_kind(value.stor)); 1600 | } 1601 | json_traits_t::rebind_destroy_deallocate(alloc, ptr); 1602 | } 1603 | default:; 1604 | } 1605 | } 1606 | 1607 | static void copy_variant(decltype(Node::stor) const& from, decltype(Node::stor)& to, allocator_type& alloc) 1608 | { 1609 | using enum detail::json_kind_t; 1610 | 1611 | switch (json_traits_t::get_kind(from)) 1612 | { 1613 | case undefined: 1614 | break; 1615 | case null: 1616 | json_traits_t::set_undefined(to); 1617 | break; 1618 | case boolean: 1619 | json_traits_t::set_boolean(to, json_traits_t::template get_val(from)); 1620 | break; 1621 | case number: 1622 | json_traits_t::set_number(to, json_traits_t::template get_val(from)); 1623 | break; 1624 | case integer: 1625 | if constexpr (has_integer) 1626 | json_traits_t::set_integer( 1627 | to, json_traits_t::template get_val(from)); 1628 | break; 1629 | case uinteger: 1630 | if constexpr (has_uinteger) 1631 | json_traits_t::set_uinteger( 1632 | to, json_traits_t::template get_val(from)); 1633 | break; 1634 | case string: 1635 | json_traits_t::set_string(to, alloc, json_traits_t::template get_val(from)); 1636 | break; 1637 | case array: 1638 | json_traits_t::set_array(to, alloc, std::from_range, 1639 | json_traits_t::template get_val(from) 1640 | | std::views::transform([](node_type const& lhs) static { 1641 | node_type rhs{ lhs.alloc }; 1642 | copy_variant(lhs.stor, rhs.stor, rhs.alloc); 1643 | 1644 | return rhs; 1645 | })); 1646 | break; 1647 | case object: 1648 | json_traits_t::set_object(to, alloc, std::from_range, 1649 | json_traits_t::template get_val(from) 1650 | | std::views::transform([](object_type::value_type const& lhs) { 1651 | auto&& [key, value] = lhs; 1652 | typename object_type::value_type rhs{ key, { value.alloc } }; 1653 | auto&& [_, out_value] = rhs; 1654 | copy_variant(value.stor, out_value.stor, out_value.alloc); 1655 | 1656 | return rhs; 1657 | })); 1658 | break; 1659 | } 1660 | } 1661 | }; 1662 | 1663 | enum class json_option : unsigned int 1664 | { 1665 | // for deserializer 1666 | allow_null = 0x0, 1667 | allow_comment = 0x1, 1668 | allow_overflow_number = 0x2, 1669 | allow_underflow_number = 0x4, 1670 | allow_overflow_integer = 0x8, 1671 | allow_underflow_integer = 0x10, 1672 | allow_overflow_uinteger = 0x20, 1673 | treat_overflow_integer_as_number = 0x40, 1674 | // for reflector 1675 | treat_null_as_defaulted = 0x80, 1676 | treat_undefined_as_defaulted = 0x100, 1677 | // for serializer 1678 | treat_undefined_as_null = 0x200, 1679 | treat_undefined_as_undefined = 0x400, 1680 | treat_undefined_as_literal = 0x800, // debug only 1681 | }; 1682 | } // namespace bizwen 1683 | 1684 | // these class templates have nested allocator_type, but shoudn't be uses-allocator constructed 1685 | 1686 | template 1687 | struct std::uses_allocator, Alloc>: std::false_type 1688 | { 1689 | }; 1690 | 1691 | template 1692 | struct std::uses_allocator, Alloc>: std::false_type 1693 | { 1694 | }; 1695 | 1696 | #endif // defined(BIZWEN_BASIC_JSON_HPP) 1697 | -------------------------------------------------------------------------------- /proposal.bs: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | 28 | # Motivation 29 | 30 | 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. 31 | 32 | 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++. 33 | 34 | There are many third-party libraries that provide JSON support for C++, but they have some drawbacks, such as: 35 | - They may have external dependencies, which can increase the complexity and size of the project, and introduce potential errors and incompatibilities. 36 | - 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. 37 | - 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. 38 | - 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. 39 | 40 | Therefore, this proposal aims to provide a minimal JSON support library for C++, which can address these issues, and offer the following benefits: 41 | 42 | - It does not have any external dependencies, and can be easily integrated into any C++ project, without affecting the existing code or environment. 43 | - 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. 44 | - 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. 45 | - It is easy to implement, with only about 2200 lines of code, which avoids too much implementation details and problems. 46 | 47 | # Proposal 48 | 49 | I propose to add a `` header and the following classes (templates): `nulljson_t`, `basic_json_node`, `basic_json`, `basic_const_json_slice`, `basic_json_slice`. 50 | The `json_errc` enumeration and the `json_error` exception class are used to report errors. 51 | 52 | ```cpp 53 | struct nulljson_t; 54 | 55 | inline constexpr nulljson_t nulljson{}; 56 | 57 | template 59 | class basic_json_node; 60 | 61 | template 64 | class basic_json; 65 | 66 | template 69 | class basic_const_json_slice; 70 | 71 | template 74 | class basic_json_slice; 75 | 76 | enum class errc; 77 | 78 | class json_error; 79 | ``` 80 | 81 | # Design 82 | 83 | Since JSON has a self-referential structure ([[RFC8259]]), type erasure must be used. 84 | 85 | json.org’s JSON structure diagram: object 86 | json.org’s JSON structure diagram: value 87 | 88 | ## `nulljson`/`nulljson_t` 89 | 90 | `nulljson` is a type similar to `nullopt`, used to indicate that the value of JSON is null. 91 | 92 | `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. 93 | 94 | ## `basic_json_node` 95 | 96 | `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. 97 | 98 | 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. 99 | 100 | `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. 101 | 102 | `basic_json_node` is a substitute for `basic_json`, providing storage space for `basic_json` in any situation where circular dependencies may occur. 103 | 104 | `basic_json_node` is conceptually similar to a raw pointer. It does not always own memory, but can transfer memory through it. 105 | 106 | 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. 107 | 108 | ## `basic_json` 109 | 110 | `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. 111 | 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. 112 | 113 | 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), 114 | 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`. 115 | 116 | `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. 117 | 118 | 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, 119 | 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. 120 | 121 | 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: 122 | 123 | relationship diagram between node, json and slice 124 | 125 | Therefore, the `Allocator` template parameter of `basic_json_node` is not used directly, but always rebound to the allocators of string, array, and object. 126 | 127 | Most of the member functions of `basic_json` are constructors, which make C++ values easily convertible to a JSON type. 128 | 129 | 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`. 130 | 131 | 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, 132 | 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> a` and `std::map m`, then construct `basic_json` objects through the constructors of boolean or arithmetic types, 133 | 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. 134 | 135 | ## `basic_json_slice`/`basic_const_json_slice` 136 | 137 | `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. 138 | 139 | `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. 140 | 141 | `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. 142 | 143 | `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. 144 | 145 | 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. 146 | 147 | ## Summary 148 | 149 | 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. 150 | 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. 151 | 152 | This design does not care whether the string type has a charactor traits or allocator type. 153 | 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. 154 | This makes it possible to not copy any strings (but still need to use dynamic memory to store maps and arrays). 155 | 156 | # Implementation experience 157 | 158 | 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. 159 | 160 | 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. 161 | 162 | The design is feasible and stable, but I need some feedback to appropriately increase the usability of the library. 163 | 164 | # Use cases 165 | 166 | ```cpp 167 | #include "basic_json.hpp" 168 | #include 169 | #include 170 | int main() 171 | { 172 | using json = bizwen::json; 173 | using node = bizwen::json_node; 174 | using slice = bizwen::json_slice; 175 | using const_slice = bizwen::const_json_slice; 176 | auto null = bizwen::nulljson; 177 | using namespace std::literals; 178 | // A json object represents a json value. The default constructed json object does not hold any value, and its state is "undefined". 179 | json j01{}; 180 | // Construct a json value with status "number" and value `1`. 181 | json j02{ 1. }; 182 | // Construct a json value with status "true_value" and value `true`. 183 | json j03{ true }; 184 | // Construct a json value with status "uinteger" and value `1`. This method is used to accurately store integers with more than 52 bits. 185 | json j04{ 1ull }; 186 | // Construct a json value with status "string" and value `"abcdef"`. 187 | json j05{ "abcdef" }; 188 | // Construct json with nullptr is forbidden because json does not have a pointer type and nullptr does not represent the empty string. 189 | // json j05{ nullptr }; 190 | // Construct a json value with status "null" and value `nulljson`. 191 | json j06{ null }; 192 | // Since initializer_list returns a reference to a const object, this method is inefficient. 193 | // json j07{ json::array_type{ json{0}, json{1} } }; 194 | // json j08{ json::object_type{ { "key0"s, json{ 0 } }, { "key1"s, json{ 1 } } } }; 195 | // Use the helper class templates json::array and json::object for easy and efficient construction of json arrays and json objects. 196 | // Constructs a json value with the state "array", containing two ordered values 0 and 1. 197 | json j07{ json::array{ 0, 1 } }; 198 | // Construct a json value with state "object" such that `s08["key0"]==0` and `s08["key1"]==1` is true. 199 | json j08{ json::object{ "key0"s, 0, "key1"s, 1 } }; 200 | // Copy a json object copies its stored state and values. 201 | auto j09{ j08 }; 202 | 203 | // slice is an accessor and modifier of json values, and the default constructed slice is not associated with any json. 204 | slice s01{}; 205 | // Use empty() to test if slice is associated with a json. 206 | auto is_empty{ s01.empty() }; 207 | assert(is_empty); // does nothing 208 | slice s07{ j07 }; // s07 is associated to j07 209 | auto is_array{ s07.array() }; 210 | assert(is_array); // does nothing 211 | // Convert a json value (s01) with state "undefined" to "integer" and set the value to `1`. 212 | s01 = 1; 213 | slice s02{ j02 }; // s02 is associated to j02 214 | // Change the value of j02 to 2.f 215 | s02 = 2.f; 216 | // Sets the state of j02 to "undefined" and destroys the previous value. 217 | s02.reset(); 218 | long long iv{ s07[0] }; 219 | 220 | // Iterate j07 221 | for (auto i : s07.as_array()) 222 | { 223 | assert(i.integer()); // does nothing 224 | } 225 | // Append a value to j07 226 | // Due to CWG1996, list initialization cannot be used here. 227 | json::array_type& arr( s07 ); 228 | arr.push_back(json{ 2 }); 229 | slice s08{ j08 }; 230 | // Iterate j08 231 | for (auto const& [k, v] : s08.as_object()) 232 | { 233 | assert(v.integer()); // does nothing 234 | } 235 | // Insert k-v pairs into j08 236 | s08["key3"] = 2; 237 | // Copying a slice is trivial 238 | auto s08_1{ s08 }; 239 | 240 | // const_slice is similar to slice, but has only observers and never modifies the associated json. 241 | const_slice c01{ s01 }; 242 | // Unlike slice, if the key does not exist in the object then a json_error exception is thrown. 243 | try 244 | { 245 | c01["keyn"]; 246 | } 247 | catch (bizwen::json_error const& je) 248 | { 249 | assert(je.code() == bizwen::json_errc::key_not_found); // does nothing 250 | } 251 | } 252 | ``` 253 | 254 | # Library wording 255 | 256 | ## JSON processing 257 | 258 | [*Drafting note:* Add this subclause to [utilities]. -- *end drafting note*] 259 | 260 | ### Header `` synopsis 261 | 262 | ```cpp 263 | namespace std { 264 | struct nulljson_t { 265 | explicit nulljson_t() = default; 266 | }; 267 | 268 | inline constexpr nulljson_t nulljson; 269 | 270 | template> 272 | class basic_json_node; 273 | 274 | template, class String = string, class Array = vector, 275 | class Object = map, bool HasInteger = true, bool HasUInteger = true> 276 | class basic_json; 277 | 278 | template, class String = string, class Array = vector, 279 | class Object = map, bool HasInteger = true, bool HasUInteger = true> 280 | class basic_json_slice; 281 | 282 | template, class String = string, class Array = vector, 283 | class Object = map, bool HasInteger = true, bool HasUInteger = true> 284 | class basic_const_json_slice; 285 | 286 | using json = basic_json<>; 287 | using json_node = basic_json_node<>; 288 | using json_slice = basic_json_slice<>; 289 | using const_json_slice = basic_const_json_slice<>; 290 | 291 | enum class json_errc : unspecified; 292 | 293 | class json_error; 294 | 295 | namespace pmr { 296 | template 298 | using basic_json_node = std::basic_json_node>; 300 | 301 | template, class String = string, class Array = vector, 302 | class Object = map, bool HasInteger = true, bool HasUInteger = true> 303 | using basic_json = std::basic_json; 304 | 305 | template, class String = string, class Array = vector, 306 | class Object = map, bool HasInteger = true, bool HasUInteger = true> 307 | using basic_json_slice = std::basic_json_slice; 309 | 310 | template, class String = string, class Array = vector, 311 | class Object = map, bool HasInteger = true, bool HasUInteger = true> 312 | using basic_const_json_slice = std::basic_const_json_slice; 314 | 315 | using json = basic_json<>; 316 | using json_node = basic_json_node<>; 317 | using json_slice = basic_json_slice<>; 318 | using const_json_slice = basic_const_json_slice<>; 319 | } 320 | } 321 | ``` 322 | 323 | ### General 324 | 325 | 1. A `basic_json_node` object is a JSON node. A valid node object is always in one of the following states: 326 | undefined, null, true, false, floating-point, signed integral, unsigned integral, string, array, and object. 327 | 328 | 2. Some operations of `basic_json` and `basic_json_slice` dynamically allocate memory. 329 | This happens if and only if the `basic_json_node` held by `basic_json` or referenced by `basic_json_slice` is set to 330 | the string, array, or object state. In such a case, let 331 | - `A` be the `allocator_type` of the node type, 332 | - `a` be the allocator object of type `A` stored in the node, 333 | - `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. 334 | - `AX` be `allocator_traits::template rebind_alloc`, 335 | - `ax` be an lvalue of type `AX` that denotes an `AX` allocator object constructed from `a`. 336 | 337 | Such an aforementioned operation allocates the dynamic storage with `allocator_traits::allocate(ax, 1)`, 338 | and then constructs the `X` object using `allocator_traits::construct(ax, p, args...)`. 339 | If an exception is thrown when setting the state, the value of the node is unchanged. 340 | 341 | 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. 342 | Let 343 | - `x` be the `X` object to be destroyed, 344 | - `pp` be `pointer_traits::pointer>::pointer_to(x)`, 345 | 346 | `x` is destroyed using `allocator_traits::destroy(ax, addressof(x))`, 347 | and then the dynamic storage is deallocated with `allocator_traits::deallocate(pp, 1)`. 348 | 349 | 4. For the purpose of constraints of functions, the map type `Object` is considered transparently comparable if and only if 350 | - *qualified-id*s `Object::key_compare` and `Object::key_compare::is_transparent` are all valid and denote types, or 351 | - *qualified-id*s `Object::key_equal`, `Object::key_equal::is_transparent`, `Object::hasher`, and `Object::hasher::is_transparent` 352 | are all valid and denote types. 353 | 354 | ### Class template `basic_json_node` 355 | 356 |
 357 | namespace std {
 358 |   template<class Number = double, class Integer = long long, class UInteger = unsigned long long,
 359 |            class Allocator = allocator>
 360 |     class basic_json_node {
 361 |     private:
 362 |       using void-ptr = allocator_traits::void_pointer;   // exposition only
 363 | 
 364 |     public:
 365 |       using number_type    = Number;
 366 |       using integer_type   = Integer;
 367 |       using uinteger_type  = UInteger;
 368 |       using allocator_type = Allocator;
 369 | 
 370 |       constexpr basic_json_node() noexcept(is_nothrow_default_constructible_v)
 371 |         requires default_initializable<Allocator>;
 372 |       constexpr explicit basic_json_node(const Allocator& a) noexcept;
 373 | 
 374 |       constexpr basic_json_node(const basic_json_node& other) noexcept;
 375 |       constexpr basic_json_node(basic_json_node&& other) noexcept;
 376 |       constexpr basic_json_node& operator=(const basic_json_node& other)
 377 |         noexcept(is_nothrow_copy_assignable_v<Allocator>);
 378 |       constexpr basic_json_node& operator=(basic_json_node&& other)
 379 |         noexcept(is_nothrow_move_assignable_v<Allocator>);
 380 |       constexpr ~basic_json_node() noexcept;
 381 |     };
 382 | 
 383 |   template<class Number, class Integer, class UInteger, class Allocator, class Allocator2>
 384 |     struct uses_allocator<basic_json_node<Number, Integer, UInteger, Allocator>, Allocator2> :
 385 |       false_type {};
 386 | }
 387 | 
388 | 389 | 1. *Mandates*: 390 | - `floating_point`, `signed_integral`, and `unsigned_integral` are all `true`. 391 | - `Allocator` meets the *Cpp17Allocator* requirements, no diagnostic is required for this. 392 | 393 | 2. If the node is in the floating-point, signed integral, or unsigned integral state, 394 | an object of type `number_type`, `integer_type`, or `uinteger_type` stored within the node. 395 | If the node is in the string, array, or object state, an object of the type corresponding to the state is dynamically allocated, 396 | and a void-ptr to it is stored within the node. 397 | [*Note*: A single node type can refer to string, dynamic array, or map of different types. -- *end note*] 398 | 399 | 3. [*Note*: The `uses_allocator` partial specialization indicates that `basic_json_node` should not be uses-allocator constructed. -- *end note*] 400 | 401 | #### Construction, assignment, and destruction 402 | 403 | ```cpp 404 | constexpr basic_json_node() noexcept(is_nothrow_default_constructible_v) 405 | requires default_initializable; 406 | ``` 407 | 408 | 1. *Effects*: Initializes the node to the undefined state. Value-initializes the stored allocator. 409 | 410 | ```cpp 411 | constexpr explicit basic_json_node(const Allocator& a) noexcept; 412 | ``` 413 | 414 | 2. *Effects*: Initializes the node to the undefined state. Copy-constructs the stored allocator from `a`. 415 | 416 | ```cpp 417 | constexpr basic_json_node(const basic_json_node& other) noexcept; 418 | constexpr basic_json_node(basic_json_node&& other) noexcept; 419 | ``` 420 | 421 | 3. *Effects*: 422 | - For the copy constructor, copy-constructs the stored allocator from that of `other`; for the move constructor, move construction is used instead. 423 | - Sets `*this` in the same state as `other`. 424 | - If `other` stores a numeric value, stores the same value in `this`. 425 | - Otherwise, if `other` stores a void-ptr to an allocated object, 426 | for the copy constructor, copy-constructs that pointer into `*this`; for the move constructor, move construction is used instead. 427 | 428 | 4. *Remarks*: 429 | - The copy constructor is trivial if both `is_trivially_copy_constructible_v` and is_trivially_copy_constructible_v<void-ptr> are `true`. 430 | - The move constructor is trivial if both `is_trivially_move_constructible_v` and is_trivially_move_constructible_v<void-ptr> are `true`. 431 | 432 | ```cpp 433 | constexpr basic_json_node& operator=(const basic_json_node& other) 434 | noexcept(is_nothrow_copy_assignable_v); 435 | constexpr basic_json_node& operator=(basic_json_node&& other) 436 | noexcept(is_nothrow_move_assignable_v); 437 | ``` 438 | 439 | 5. *Constraints*: For the move assignment operator, `is_move_assignable_v` is `true`. 440 | 441 | 6. *Effects*: 442 | - If `this == addressof(other)`, does nothing. 443 | - Otherwise, copy- or move-assign the stored allocator of `*this` from that of `other`, for copy or move assignment operator respectively. And then 444 | - if `*this` is in the same state as `other` and stores a numeric value or void-ptr, 445 | copy- or move-assign the stored numeric value or void-ptr in `other` to that of `*this`, for copy or move assignment operator respectively, otherwise, 446 | - destroys the stored a numeric value or void-ptr, if any, and then 447 | copy- or move-constructs the numeric value or void-ptr from that of `other` for copy or move assignment operator respectively, if any. 448 | 449 | 7. *Returns*: `*this`. 450 | 451 | 8. *Remarks*: 452 | - The copy assignment operator is deleted if `is_copy_assignable_v` is `false`. 453 | - The copy assignment operator is trivial if `is_trivially_copy_assignable_v`, is_trivially_destructible_v<void-ptr>, is_trivially_copy_constructible_v<void-ptr>, and is_trivially_copy_assignable_v<void-ptr> are all `true`. 454 | - The move assignment operator is trivial if `is_trivially_move_assignable_v`, is_trivially_destructible_v<void-ptr>, is_trivially_move_constructible_v<void-ptr>, and is_trivially_move_assignable_v<void-ptr> are all `true`. 455 | 456 | ```cpp 457 | constexpr ~basic_json_node() noexcept; 458 | ``` 459 | 460 | 9. *Effects*: Destroys the stored allocator, and the numeric value or the void-ptr, if any. [*Note*: No deallocation happens. -- *end note*] 461 | 462 | 10. *Remarks*: The destructor is trivial if both `is_trivially_destructible_v` and is_trivially_destructible_v<void-ptr> are `true`. 463 | 464 | ### Class template `basic_json` 465 | 466 |
 467 | namespace std {
 468 |   template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
 469 |            class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
 470 |     class basic_json {
 471 |     public:
 472 |       static constexpr bool has_integer  = HasInteger;
 473 |       static constexpr bool has_uinteger = HasUInteger;
 474 | 
 475 |       using slice_type       = basic_json_slice<Node, String, Array, Object,
 476 |                                                 HasInteger, HasUInteger>;
 477 |       using const_slice_type = basic_const_json_slice<Node, String, Array, Object,
 478 |                                                       HasInteger, HasUInteger>;
 479 | 
 480 |       using number_type   = node_type::number_type;
 481 |       using integer_type  = node_type::integer_type;
 482 |       using uinteger_type = node_type::uinteger_type;
 483 | 
 484 |       using char_type       = string_type::value_type;
 485 |       using map_node_type   = object_type::value_type;
 486 |       using allocator_type  = node_type::allocator_type;
 487 |       using key_string_type = object_type::key_type;
 488 |       using key_char_type   = key_string_type::value_type;
 489 | 
 490 |       constexpr basic_json() requires default_initializable = default;
 491 |       constexpr explicit basic_json(const allocator_type& a) noexcept : node_(a) {}
 492 |       constexpr basic_json(const basic_json& rhs);
 493 |       constexpr basic_json(basic_json&& rhs) noexcept;
 494 |       constexpr basic_json& operator=(const basic_json& rhs);
 495 |       constexpr basic_json& operator=(basic_json&& rhs)
 496 |         noexcept(allocator_traits::propagate_on_container_move_assignment::value ||
 497 |                 allocator_traits::is_always_equal::value);
 498 |       constexpr ~basic_json();
 499 |       constexpr void swap(basic_json& rhs)
 500 |         noexcept(allocator_traits::propagate_on_container_swap::value ||
 501 |                  allocator_traits::is_always_equal::value);
 502 |       friend constexpr void swap(basic_json& lhs, basic_json& rhs)
 503 |         noexcept(noexcept(lhs.swap(rhs)));
 504 | 
 505 |       constexpr basic_json(const basic_json& rhs, const allocator_type& a);
 506 |       constexpr basic_json(basic_json&& rhs, const allocator_type& a)
 507 |         noexcept(allocator_traits::is_always_equal::value);
 508 | 
 509 |       constexpr basic_json(nulljson_t, const allocator_type& a = allocator_type()) noexcept;
 510 |       template<class T>
 511 |         constexpr basic_json(T n, const allocator_type& a = allocator_type()) noexcept;
 512 |       constexpr explicit basic_json(string_type v, const allocator_type& a = allocator_type());
 513 |       constexpr basic_json(const char_type* first, const char_type* last,
 514 |                            const allocator_type& a = allocator_type());
 515 |       constexpr basic_json(const char_type* str, string_type::size_type count,
 516 |                            const allocator_type& a = allocator_type());
 517 |       constexpr explicit basic_json(const char_type* str,
 518 |                                     const allocator_type& a = allocator_type());
 519 |       template<class StrLike>
 520 |         constexpr basic_json(const StrLike& str, const allocator_type& a = allocator_type());
 521 |       constexpr explicit basic_json(array_type arr,
 522 |                                     const allocator_type& a = allocator_type());
 523 |       constexpr explicit basic_json(object_type obj,
 524 |                                     const allocator_type& a = allocator_type());
 525 |       constexpr explicit basic_json(node_type&& n) noexcept;
 526 |       constexpr explicit basic_json(node_type&& n, const allocator_type& a) noexcept;
 527 |       basic_json(nullptr_t, const allocator_type& = allocator_type()) = delete;
 528 | 
 529 |       constexpr allocator_type get_allocator() const noexcept;
 530 | 
 531 |       constexpr operator node_type() && noexcept;
 532 |       constexpr slice_type slice() noexcept;
 533 |       constexpr const_slice_type slice() const noexcept;
 534 | 
 535 |     private:
 536 |       node_type node_;   // exposition only
 537 | 
 538 |       // Helper Classes
 539 |       template<class... Ts>
 540 |       struct array;      // exposition only
 541 | 
 542 |       template<size_t N>
 543 |       struct object;     // exposition only
 544 | 
 545 |       template<class... Ts>
 546 |       object(Ts&&...) -> object<see below>;
 547 |     };
 548 | }
 549 | 
550 | 551 | 1. *Mandates*: 552 | - `Node` is a well-formed specialization of `basic_json_node`. 553 | - *qualified-id* `Array::value_type` is valid and denotes the same type as `Node`. 554 | - *qualified-id* `Object::mapped_type` is valid and denotes the same type as `Node`. 555 | 556 |
Construction and swap
557 | 558 | 1. Every constructor taking a `const allocator_type&` parameter constructs `node_` from that parameter. 559 | 560 | 2. Every constructor or assignment operator taking a `basic_json&&` parameter leaves the argument in a valid but unspecified state after construction or assignment. 561 | Except for the move constructor, each of these functions reuse the dynamic storage of the argument if and only if 562 | there is some dynamic storage allocated and the stored allocator in the `node_` is equal to that of the argument. 563 | 564 | 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]. 565 | 566 | ```cpp 567 | constexpr basic_json(nulljson_t, const allocator_type& a = allocator_type()) noexcept; 568 | ``` 569 | 570 | 4. *Effects*: Sets `node_` to the null state. 571 | 572 | ```cpp 573 | template 574 | constexpr basic_json(T n, const allocator_type& a = allocator_type()) noexcept; 575 | ``` 576 | 577 | 5. *Constraints*: `is_arithmetic_v` is `true`. 578 | 579 | 6. *Effects*: 580 | - If `T` is `bool`, sets `node_` to the true or the false state if `n` is `true` or `false`, respectively. 581 | - Otherwise, if `signed_integral` and `has_integer` are both `true`, sets `node_` to the signed integral state and stores `n`. 582 | - Otherwise, if `unsigned_integral` and `has_uinteger` are both `true`, sets `node_` to the unsigned integral state and stores `n`. 583 | - Otherwise, sets `node_` to the floating-point state and stores `static_cast(n)`. 584 | 585 | ```cpp 586 | constexpr explicit basic_json(string_type v, const allocator_type& a = allocator_type()); 587 | ``` 588 | 589 | 7. *Effects*: Sets `node_` to the string state and stores a string constructed from `std::move(v)`. 590 | 591 | 8. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`. 592 | 593 | ```cpp 594 | constexpr basic_json(const char_type* first, const char_type* last, const allocator_type& a = allocator_type()); 595 | ``` 596 | 597 | 9. *Preconditions*: [`first`, `last`) is a valid range. 598 | 599 | 10. *Effects*: Sets `node_` to the string state and stores a string containing characters in [`first`, `last`). 600 | 601 | 11. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`. 602 | 603 | ```cpp 604 | constexpr basic_json(const char_type* str, string_type::size_type count, const allocator_type& a = allocator_type()); 605 | ``` 606 | 607 | 12. *Preconditions*: [`str`, `str + count`) is a valid range. 608 | 609 | 13. *Effects*: Sets `node_` to the string state and stores a string containing characters in [`str`, `str + count`). 610 | 611 | 14. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`. 612 | 613 | ```cpp 614 | constexpr explicit basic_json(const char_type* str, const allocator_type& a = allocator_type()); 615 | ``` 616 | 617 | 15. *Preconditions*: `str` points to a null-terminated `char_type` sequence. 618 | 619 | 16. *Effects*: Sets `node_` to the string state and stores a string containing characters in [`str`, `str + count`), 620 | where `count` is the number of characters in the null-terminated sequence. 621 | 622 | 17. *Throws*: The exception thrown on allocation failure, or the exception thrown in the failed construction of the `String`. 623 | 624 | ```cpp 625 | template 626 | constexpr basic_json(const StrLike& str, const allocator_type& a = allocator_type()); 627 | ``` 628 | 629 | 18. *Constraints*: 630 | - `constructible_from` is `true`, and 631 | - `is_convertible_v` is `false`. 632 | 633 | 19. *Effects*: Sets `node_` to the string state and stores a string constructed from `str`. 634 | 635 | 20. *Throws*: The exception thrown on allocation failure, or the exception thrown in by the construction of the `String`. 636 | 637 | ```cpp 638 | constexpr explicit basic_json(array_type arr, const allocator_type& a = allocator_type()); 639 | ``` 640 | 641 | 21. *Effects*: Sets `node_` to the array state and stores a dynamic array constructed from `std::move(arr)`. 642 | 643 | 22. *Throws*: The exception thrown on allocation failure, or the exception thrown by the construction of the `Array`. 644 | 645 | ```cpp 646 | constexpr explicit basic_json(object_type obj, const allocator_type& a = allocator_type()); 647 | ``` 648 | 649 | 23. *Effects*: Sets `node_` to the object state and stores a map constructed from `std::move(obj)`. 650 | 651 | 24. *Throws*: The exception thrown on allocation failure, or the exception thrown by the construction of the `Object`. 652 | 653 | ```cpp 654 | constexpr explicit basic_json(node_type&& n) noexcept; 655 | ``` 656 | 657 | 25. *Effects*: Constructs `node_` from `std::move(n)` and sets `n` to the undefined state. 658 | The stored allocator in `n` is unchanged. 659 | 660 | ```cpp 661 | constexpr explicit basic_json(node_type&& n, const allocator_type& a) noexcept; 662 | ``` 663 | 664 | 25. *Effects*: Constructs `node_` from `std::move(a)`, sets it to the same state as `n`, and then sets `n` to the undefined state. 665 | The stored allocator in `n` is unchanged. 666 | 667 | ```cpp 668 | constexpr void swap(basic_json& rhs) 669 | noexcept(allocator_traits::propagate_on_container_swap::value || 670 | allocator_traits::is_always_equal::value); 671 | friend constexpr void swap(basic_json& lhs, basic_json& rhs) noexcept(noexcept(lhs.swap(rhs))); 672 | ``` 673 | 674 | 26. *Effects*: Swaps the values of the `node_` member of both `basic_json` objects. 675 | 676 |
Observers
677 | 678 | ```cpp 679 | constexpr allocator_type get_allocator() const noexcept; 680 | ``` 681 | 682 | 1. *Returns*: The copy of the constructor stored in `node_`. 683 | 684 | #### Slicing 685 | 686 | ```cpp 687 | constexpr operator node_type() && noexcept; 688 | ``` 689 | 690 | 1. *Effects*: Sets `node_` to the undefined state. 691 | 692 | 2. *Returns*: A `node_type` value holding the value of `node_` before modification. 693 | 694 | 3. *Recommended practice*: An implementation should emit a diagnostic message when the return value is discarded.
695 | [*Note*: Discarding the return value leaks memory if `node_` was originally in the string, array, or object state. -- *end note*] 696 | 697 | ```cpp 698 | constexpr slice_type slice() noexcept; 699 | ``` 700 | 701 | 4. *Effects*: Equivalent to `return slice_type(*this);`. 702 | 703 | ```cpp 704 | constexpr const_slice_type slice() const noexcept; 705 | ``` 706 | 707 | 5. *Effects*: Equivalent to `return const_slice_type(*this);`. 708 | 709 | ### Class template `basic_json_slice` 710 | 711 |
 712 | namespace std {
 713 |   template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
 714 |            class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
 715 |     class basic_json_slice {
 716 |     public:
 717 |       static constexpr bool has_integer  = HasInteger;
 718 |       static constexpr bool has_uinteger = HasUInteger;
 719 | 
 720 |       using node_type   = Node;
 721 |       using object_type = Object;
 722 |       using value_type  = Node;
 723 |       using array_type  = Array;
 724 |       using string_type = String;
 725 | 
 726 |       using slice_type       = basic_json_slice;
 727 |       using const_slice_type = basic_const_json_slice<Node, String, Array, Object,
 728 |                                                       HasInteger, HasUInteger>;
 729 |       using json_type        = basic_json<Node, String, Array, Object,
 730 |                                           HasInteger, HasUInteger>;
 731 | 
 732 |       using number_type   = node_type::number_type;
 733 |       using integer_type  = node_type::integer_type;
 734 |       using uinteger_type = node_type::uinteger_type;
 735 | 
 736 |       using char_type       = string_type::value_type;
 737 |       using map_node_type   = object_type::value_type;
 738 |       using allocator_type  = node_type::allocator_type;
 739 |       using key_string_type = object_type::key_type;
 740 |       using key_char_type   = key_string_type::value_type;
 741 | 
 742 |       constexpr basic_json_slice() noexcept = default;
 743 |       constexpr basic_json_slice(json_type& j) noexcept;
 744 |       constexpr basic_json_slice(node_type& n) noexcept;
 745 |       constexpr void swap(basic_json_slice& rhs) noexcept;
 746 |       friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept;
 747 | 
 748 |       constexpr basic_json_slice& operator=(json_type& j) noexcept;
 749 |       constexpr basic_json_slice& operator=(node_type& n) noexcept;
 750 | 
 751 |       constexpr bool empty() const noexcept;
 752 |       constexpr bool undefined() const noexcept;
 753 |       constexpr bool string() const noexcept;
 754 |       constexpr bool null() const noexcept;
 755 |       constexpr bool boolean() const noexcept;
 756 |       constexpr bool number() const noexcept;
 757 |       constexpr bool object() const noexcept;
 758 |       constexpr bool array() const noexcept;
 759 |       constexpr bool integer() const noexcept;
 760 |       constexpr bool uinteger() const noexcept;
 761 | 
 762 |       constexpr explicit operator bool() const;
 763 |       constexpr explicit operator number_type() const;
 764 |       constexpr explicit operator nulljson_t() const;
 765 |       constexpr explicit operator const string_type&() const&;
 766 |       constexpr explicit operator const array_type&() const&;
 767 |       constexpr explicit operator const object_type&() const&;
 768 |       constexpr explicit operator integer_type() const;
 769 |       constexpr explicit operator uinteger_type() const;
 770 |       template<integral I>
 771 |         constexpr basic_const_json_slice operator[](I pos) const;
 772 |       constexpr basic_const_json_slice operator[](const key_string_type& k) const;
 773 |       template<class KeyStrLike>
 774 |         constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
 775 |       constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
 776 | 
 777 |       constexpr void reset() noexcept;
 778 |       constexpr basic_json_slice& operator=(nulljson_t);
 779 |       template<class T>
 780 |         constexpr basic_json_slice& operator=(T n);
 781 |       constexpr basic_json_slice& operator=(const string_type& str);
 782 |       constexpr basic_json_slice& operator=(string_type&& str);
 783 |       constexpr basic_json_slice& operator=(const char_type* str);
 784 |       template<class StrLike>
 785 |         constexpr basic_json_slice& operator=(const StrLike& str);
 786 |       template<integral I>
 787 |         constexpr basic_json_slice operator[](I pos);
 788 |       constexpr basic_json_slice operator[](const key_string_type& k);
 789 |       template<class KeyStrLike>
 790 |         constexpr basic_json_slice operator[](const KeyStrLike& k);
 791 |       constexpr basic_json_slice operator[](const key_char_type* ks);
 792 | 
 793 |     private:
 794 |       node_type* node_ = nullptr;    // exposition only
 795 |     };
 796 | 
 797 |   template<class Node, class String, class Array, class Object,
 798 |            bool HasInteger, bool HasUInteger, class Allocator>
 799 |     struct uses_allocator<basic_json_slice<Node, String, Array, Object,
 800 |                                               HasInteger, HasUInteger>, Allocator2> :
 801 |       false_type {};
 802 | }
 803 | 
804 | 805 | 1. Every specialization of `basic_json_slice` is a trivally copyable class that models `semiregular`. 806 | 807 | 2. A `basic_json_slice` is considered valid if its `node_` member points to a `node_type` object within its lifetime.
808 | [*Note*: A default-constructed `basic_json_slice` is not valid. -- *end note*] 809 | 810 | 3. [*Note*: The default constructor of `basic_json_slice` is intentionally used for default arguments. -- *end note*] 811 | 812 | 4. Whenever `basic_json_slice` is valid, its referenced node is the object denoted by *node_. 813 | 814 | 5. [*Note*: The `uses_allocator` partial specialization indicates that `basic_json_slice` should not be uses-allocator constructed. -- *end note*] 815 | 816 |
Construction and swap
817 | 818 | ```cpp 819 | constexpr basic_json_slice(json_type& j) noexcept; 820 | ``` 821 | 822 | 1. *Effects*: Initializes `node_` with addressof(j.node_). 823 | 824 | ```cpp 825 | constexpr basic_json_slice(node_type& n) noexcept; 826 | ``` 827 | 828 | 2. *Effects*: Initializes `node_` with `addressof(n)`. 829 | 830 | ```cpp 831 | constexpr void swap(basic_json_slice& rhs) noexcept; 832 | friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept; 833 | ``` 834 | 835 | 3. *Effects*: Swaps the values of the `node_` member of both `basic_json_slice` objects. 836 | 837 | #### Rebinding 838 | 839 | ```cpp 840 | constexpr basic_json_slice& operator=(json_type& j) noexcept; 841 | ``` 842 | 843 | 1. *Effects*: Equivalent to `return *this = basic_json_slice{j};`. 844 | 845 | ```cpp 846 | constexpr basic_json_slice& operator=(node_type& n) noexcept; 847 | ``` 848 | 849 | 2. *Effects*: Equivalent to `return *this = basic_json_slice{n};`. 850 | 851 |
Observers
852 | 853 |
 854 | constexpr bool empty() const noexcept;
 855 | constexpr bool undefined() const noexcept;
 856 | constexpr bool string() const noexcept;
 857 | constexpr bool null() const noexcept;
 858 | constexpr bool boolean() const noexcept;
 859 | constexpr bool number() const noexcept;
 860 | constexpr bool object() const noexcept;
 861 | constexpr bool array() const noexcept;
 862 | constexpr bool integer() const noexcept;
 863 | constexpr bool uinteger() const noexcept;
 864 | 
 865 | constexpr explicit operator bool() const;
 866 | constexpr explicit operator number_type() const;
 867 | constexpr explicit operator nulljson_t() const;
 868 | constexpr explicit operator const string_type&() const&;
 869 | constexpr explicit operator const array_type&() const&;
 870 | constexpr explicit operator const object_type&() const&;
 871 | constexpr explicit operator integer_type() const;
 872 | constexpr explicit operator uinteger_type() const;
 873 | template<integral I>
 874 |   constexpr basic_const_json_slice operator[](I pos) const;
 875 | constexpr basic_const_json_slice operator[](const key_string_type& k) const;
 876 | template<class KeyStrLike>
 877 |   constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
 878 | constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
 879 | 
880 | 881 | 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` 882 | and then calling the corresponding member function of `basic_const_json_slice`. 883 | 884 | #### Modifiers 885 | 886 | 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. 887 | A `json_error` exception is thrown if one attempts to change the established non-undefined state of the node. 888 | 889 | ```cpp 890 | constexpr void reset() noexcept; 891 | ``` 892 | 893 | 2. *Preconditions*: `*this` is valid. 894 | 895 | 3. *Effects*: Sets the referenced node to the undefined state. 896 | 897 | ```cpp 898 | constexpr basic_json_slice& operator=(nulljson_t); 899 | ``` 900 | 901 | 4. *Preconditions*: `*this` is valid. 902 | 903 | 5. *Effects*: 904 | - If the referenced node is in the undefined state, sets it to the null state. 905 | - Otherwise, if the referenced node is in the null state, does nothing. 906 | 907 | 6. *Returns*: `*this`. 908 | 909 | 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. 910 | 911 | ```cpp 912 | template 913 | constexpr basic_json_slice& operator=(T n); 914 | ``` 915 | 916 | 8. *Constraints*: `is_arithmetic_v` is `true`. 917 | 918 | 9. *Preconditions*: `*this` is valid. 919 | 920 | 10. *Effects*: 921 | - If the referenced node is in the undefined state, performs *node_ = json_type{n}. 922 | - Otherwise, if 923 | - `T` is `bool`, `n` is `true`, and the referenced node is in the true state, or 924 | - `T` is `bool`, `n` is `false`, and the referenced node is in the false state, 925 | 926 | then does nothing. 927 | - Otherwise, if `HasInteger` and `signed_integral` are both `true` and the referenced node is in the signed integral state, 928 | replaces the stored signed integer value with `n`. 929 | - Otherwise, if `HasUInteger` and `unsigned_integral` are both `true` and the referenced node is in the unsigned integral state, 930 | replaces the stored unsigned integer value with `n`. 931 | - Otherwise, the referenced node is in the floating-point state, replaces the stored floating-point value with `static_cast(n)`. 932 | - Otherwise, throws a `json_error` exception constructed from `json_errc::not_undefined_or_number`; 933 | 934 | 11. *Returns*: `*this`. 935 | 936 | ```cpp 937 | constexpr basic_json_slice& operator=(const string_type& str); 938 | ``` 939 | 940 | 12. *Preconditions*: `*this` is valid. 941 | 942 | 13. *Effects*: 943 | - If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from `str`. 944 | - Otherwise, if the referenced node is in the string state, assigns `str` to the stored string. 945 | 946 | 14. *Returns*: `*this`. 947 | 948 | 15. *Throws*: 949 | - 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. 950 | - Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`. 951 | 952 | ```cpp 953 | constexpr basic_json_slice& operator=(string_type&& str); 954 | ``` 955 | 956 | 16. *Preconditions*: `*this` is valid. 957 | 958 | 17. *Effects*: 959 | - If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from `std::move(str)`. 960 | - Otherwise, if the referenced node is in the string state, assigns `std::move(str)` to the stored string. 961 | 962 | 18. *Returns*: `*this`. 963 | 964 | 19. *Throws*: 965 | - 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. 966 | - Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`. 967 | 968 | ```cpp 969 | constexpr basic_json_slice& operator=(const char_type* str); 970 | ``` 971 | 972 | 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. 973 | 974 | 21. *Effects*: 975 | - 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`, 976 | where `count` is the number of characters in the null-terminated sequence. 977 | - Otherwise, if the referenced node is in the string state, replaces the contents of `str` with that of the null-terminated sequence. 978 | 979 | 22. *Returns*: `*this`. 980 | 981 | 23. *Throws*: 982 | - 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. 983 | - Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`. 984 | 985 | ```cpp 986 | template 987 | constexpr basic_json_slice& operator=(const StrLike& str); 988 | ``` 989 | 990 | 24. *Constraints*: 991 | - `constructible_from` is `true`, and 992 | - `is_convertible_v` is `false`. 993 | 994 | 25. *Preconditions*: `*this` is valid. 995 | 996 | 26. *Effects*: 997 | - If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from `str`. 998 | - Otherwise, if the referenced node is in the string state, assigns `str` to the stored string. 999 | 1000 | 27. *Returns*: `*this`. 1001 | 1002 | 28. *Throws*: 1003 | - 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. 1004 | - Otherwise, the exception thrown by the allocation, construction, or assignment of the `String`. 1005 | 1006 | ```cpp 1007 | template 1008 | constexpr basic_json_slice operator[](I pos); 1009 | ``` 1010 | 1011 | 29. *Preconditions*: `*this` is valid. If the referenced node is in the array state, `pos` is less than the length of the stored dynamic array. 1012 | 1013 | 30. *Returns*: A `basic_json_slice` referencing the node at offset `pos` of the stored dynamic array if the referenced node is in the array state. 1014 | 1015 | 31. *Throws*: A `json_error` exception constructed from `json_errc::nonarray_indexing` if the referenced node is not in the array state. 1016 | 1017 | ```cpp 1018 | constexpr basic_json_slice operator[](const key_string_type& k); 1019 | template 1020 | constexpr basic_json_slice operator[](const KeyStrLike& k); 1021 | constexpr basic_json_slice operator[](const key_char_type* ks); 1022 | ``` 1023 | 1024 | 32. *Constraints*: For the template overload: 1025 | - `Object` is transparently comparable, and 1026 | - `is_convertible_v` is `false`. 1027 | 1028 | 33. *Preconditions*: `*this` is valid. 1029 | For the overload taking a `const key_char_type*`, if the referenced node is in the object state, `ks` points to a null-terminated `key_char_type` sequence. 1030 | 1031 | 34. For the overload taking a `const key_char_type*`, let `k` be a `key_string_type` value containing characters in [`ks`, `ks + count`), 1032 | where `count` is the number of characters in the null-terminated sequence. 1033 | 1034 | 35. *Effects*: If the referenced node is in the object state and no element with key `k` is found in its stored map, 1035 | inserts an element with key `k` and a value-initialized node into the stored map. 1036 | 1037 | 36. *Returns*: 1038 | - If the referenced node is in the object state and an element with key `k` is found in its stored map, 1039 | a `basic_json_slice` referencing the mapped value of that element. 1040 | - Otherwise, if a new element `e` is inserted, a `basic_json_slice`, a `basic_json_slice` the mapped value of the inserted element. 1041 | 1042 | 37. *Throws*: 1043 | - A `json_error` exception constructed from `json_errc::nonobject_indexing` the referenced node is not in the object state. 1044 | - Otherwise, the exception thrown when looking up the key, or by the allocation or construction of the inserted map element. 1045 | 1046 | ### Class template `basic_const_json_slice` 1047 | 1048 |
1049 | namespace std {
1050 |   template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
1051 |            class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
1052 |     class basic_const_json_slice {
1053 |     public:
1054 |       static constexpr bool has_integer  = HasInteger;
1055 |       static constexpr bool has_uinteger = HasUInteger;
1056 | 
1057 |       using node_type   = Node;
1058 |       using object_type = Object;
1059 |       using value_type  = Node;
1060 |       using array_type  = Array;
1061 |       using string_type = String;
1062 | 
1063 |       using slice_type       = basic_json_slice<Node, String, Array, Object,
1064 |                                                 HasInteger, HasUInteger>;
1065 |       using const_slice_type = basic_const_json_slice;
1066 |       using json_type        = basic_json<Node, String, Array, Object,
1067 |                                           HasInteger, HasUInteger>;
1068 | 
1069 |       using number_type   = node_type::number_type;
1070 |       using integer_type  = node_type::integer_type;
1071 |       using uinteger_type = node_type::uinteger_type;
1072 | 
1073 |       using char_type       = string_type::value_type;
1074 |       using map_node_type   = object_type::value_type;
1075 |       using allocator_type  = node_type::allocator_type;
1076 |       using key_string_type = object_type::key_type;
1077 |       using key_char_type   = key_string_type::value_type;
1078 | 
1079 |       constexpr basic_const_json_slice() noexcept = default;
1080 |       constexpr basic_const_json_slice(const json_type& j) noexcept;
1081 |       constexpr basic_const_json_slice(const node_type& n) noexcept;
1082 |       constexpr basic_const_json_slice(const slice_type& s) noexcept;
1083 |       constexpr void swap(basic_const_json_slice& rhs) noexcept;
1084 |       friend constexpr void swap(basic_const_json_slice& lhs,
1085 |                                  basic_const_json_slice& rhs) noexcept;
1086 | 
1087 |       constexpr bool undefined() const noexcept;
1088 |       constexpr bool string() const noexcept;
1089 |       constexpr bool null() const noexcept;
1090 |       constexpr bool boolean() const noexcept;
1091 |       constexpr bool number() const noexcept;
1092 |       constexpr bool object() const noexcept;
1093 |       constexpr bool array() const noexcept;
1094 |       constexpr bool integer() const noexcept;
1095 |       constexpr bool uinteger() const noexcept;
1096 | 
1097 |       constexpr explicit operator bool() const;
1098 |       constexpr explicit operator number_type() const;
1099 |       constexpr explicit operator nulljson_t() const;
1100 |       constexpr explicit operator const string_type&() const&;
1101 |       constexpr explicit operator const array_type&() const&;
1102 |       constexpr explicit operator const object_type&() const&;
1103 |       constexpr explicit operator integer_type() const;
1104 |       constexpr explicit operator uinteger_type() const;
1105 |       template<integral I>
1106 |         constexpr basic_const_json_slice operator[](I pos) const;
1107 |       constexpr basic_const_json_slice operator[](const key_string_type& k) const;
1108 |       template<class KeyStrLike>
1109 |         constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
1110 |       constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
1111 | 
1112 |     private:
1113 |       const node_type* node_ = nullptr;  // exposition only
1114 |     };
1115 | 
1116 |   template<class Node, class String, class Array, class Object,
1117 |            bool HasInteger, bool HasUInteger, class Allocator>
1118 |     struct uses_allocator<basic_const_json_slice<Node, String, Array, Object,
1119 |                                                     HasInteger, HasUInteger>, Allocator2> :
1120 |       false_type {};
1121 | }
1122 | 
1123 | 1124 | 1. Every specialization of `basic_const_json_slice` is a trivally copyable class that models `semiregular`. 1125 | 1126 | 2. A `basic_const_json_slice` is considered valid if its `node_` member points to a `node_type` object within its lifetime.
1127 | [*Note*: A default-constructed `basic_const_json_slice` is not valid. -- *end note*] 1128 | 1129 | 3. [*Note*: The default constructor of `basic_const_json_slice` is intentionally used for default arguments. -- *end note*] 1130 | 1131 | 4. Whenever `basic_const_json_slice` is valid, its referenced node is the object denoted by *node_. 1132 | 1133 | 5. [*Note*: The `uses_allocator` partial specialization indicates that `basic_const_json_slice` should not be uses-allocator constructed. -- *end note*] 1134 | 1135 |
Construction and swap
1136 | 1137 | ```cpp 1138 | constexpr basic_const_json_slice(const json_type& j) noexcept; 1139 | ``` 1140 | 1141 | 1. *Effects*: Initializes `node_` with addressof(j.node_). 1142 | 1143 | ```cpp 1144 | constexpr basic_const_json_slice(const node_type& n) noexcept; 1145 | ``` 1146 | 1147 | 2. *Effects*: Initializes `node_` with `addressof(n)`. 1148 | 1149 | ```cpp 1150 | constexpr basic_const_json_slice(const slice_type& s) noexcept; 1151 | ``` 1152 | 1153 | 3. *Effects*: Initializes `node_` with s.node_.
1154 | [*Note*: `*this` and `s` reference the same node after construction if `s` is valid. -- *end note*] 1155 | 1156 | ```cpp 1157 | constexpr void swap(basic_const_json_slice& rhs) noexcept; 1158 | friend constexpr void swap(basic_const_json_slice& lhs, 1159 | basic_const_json_slice& rhs) noexcept; 1160 | ``` 1161 | 1162 | 4. *Effects*: Swaps the values of the `node_` member of both `basic_const_json_slice` objects. 1163 | 1164 |
Observers
1165 | 1166 | ```cpp 1167 | constexpr bool empty() const noexcept; 1168 | ``` 1169 | 1170 | 1. *Returns*: bool(node_).
1171 | [*Note*: `*this` is not valid when `empty()` returns `true`. -- *end note*] 1172 | 1173 | ```cpp 1174 | constexpr bool undefined() const noexcept; 1175 | ``` 1176 | 1177 | 2. *Preconditions*: `*this` is valid. 1178 | 1179 | 3. *Returns*: `true` if the referenced node is in the undefined state, `false` otherwise. 1180 | 1181 | ```cpp 1182 | constexpr bool string() const noexcept; 1183 | ``` 1184 | 1185 | 4. *Preconditions*: `*this` is valid. 1186 | 1187 | 5. *Returns*: `true` if the referenced node is in the string state, `false` otherwise. 1188 | 1189 | ```cpp 1190 | constexpr bool null() const noexcept; 1191 | ``` 1192 | 1193 | 6. *Preconditions*: `*this` is valid. 1194 | 1195 | 7. *Returns*: `true` if the referenced node is in the null state, `false` otherwise. 1196 | 1197 | ```cpp 1198 | constexpr bool boolean() const noexcept; 1199 | ``` 1200 | 1201 | 8. *Preconditions*: `*this` is valid. 1202 | 1203 | 9. *Returns*: `true` if the referenced node is in the true of the false state, `false` otherwise. 1204 | 1205 | ```cpp 1206 | constexpr bool number() const noexcept; 1207 | ``` 1208 | 1209 | 10. *Preconditions*: `*this` is valid. 1210 | 1211 | 11. *Returns*: `true` if the referenced node is in the number state, `false` otherwise. 1212 | 1213 | ```cpp 1214 | constexpr bool object() const noexcept; 1215 | ``` 1216 | 1217 | 12. *Preconditions*: `*this` is valid. 1218 | 1219 | 13. *Returns*: `true` if the referenced node is in the object state, `false` otherwise. 1220 | 1221 | ```cpp 1222 | constexpr bool array() const noexcept; 1223 | ``` 1224 | 1225 | 14. *Preconditions*: `*this` is valid. 1226 | 1227 | 15. *Returns*: `true` if the referenced node is in the array state, `false` otherwise. 1228 | 1229 | ```cpp 1230 | constexpr bool integer() const noexcept; 1231 | ``` 1232 | 1233 | 16. *Constraints*: `HasInteger` is `true`. 1234 | 1235 | 17. *Preconditions*: `*this` is valid. 1236 | 1237 | 18. *Returns*: `true` if the referenced node is in the signed integral state, `false` otherwise. 1238 | 1239 | ```cpp 1240 | constexpr bool uinteger() const noexcept; 1241 | ``` 1242 | 1243 | 19. *Constraints*: `HasUInteger` is `true`. 1244 | 1245 | 20. *Preconditions*: `*this` is valid. 1246 | 1247 | 21. *Returns*: `true` if the referenced node is in the unsigned integral state, `false` otherwise. 1248 | 1249 | ```cpp 1250 | constexpr explicit operator bool() const; 1251 | ``` 1252 | 1253 | 22. *Preconditions*: `*this` is valid. 1254 | 1255 | 23. *Returns*: `true` if the referenced node is in the true state, `false` if the referenced node is in the false state. 1256 | 1257 | 24. *Throws*: A `json_error` exception constructed from `json_errc::not_boolean` if the referenced node is not in the true or the false state. 1258 | 1259 | ```cpp 1260 | constexpr explicit operator number_type() const; 1261 | ``` 1262 | 1263 | 25. *Preconditions*: `*this` is valid. 1264 | 1265 | 26. *Returns*: The stored numeric value converted to `number_type` if the referenced node is in the floating-point, the signed integral, or the unsigned integral state. 1266 | 1267 | 27. *Throws*: A `json_error` exception constructed from `json_errc::not_number` if the referenced node is not in the floating-point, the signed integral, or the unsigned integral state. 1268 | 1269 | ```cpp 1270 | constexpr explicit operator nulljson_t() const; 1271 | ``` 1272 | 1273 | 28. *Preconditions*: `*this` is valid. 1274 | 1275 | 29. *Returns*: `nulljson_t{}` if the referenced node is in the null state. 1276 | 1277 | 30. *Throws*: A `json_error` exception constructed from `json_errc::not_null` if the referenced node is not in the null state. 1278 | 1279 | ```cpp 1280 | constexpr explicit operator const string_type&() const&; 1281 | ``` 1282 | 1283 | 31. *Preconditions*: `*this` is valid. 1284 | 1285 | 32. *Returns*: The reference to the stored string if the referenced node is in the string state. 1286 | 1287 | 33. *Throws*: A `json_error` exception constructed from `json_errc::not_string` if the referenced node is not in the string state. 1288 | 1289 | ```cpp 1290 | constexpr explicit operator const array_type&() const&; 1291 | ``` 1292 | 1293 | 34. *Preconditions*: `*this` is valid. 1294 | 1295 | 35. *Returns*: The reference to the stored dynamic array if the referenced node is in the array state. 1296 | 1297 | 36. *Throws*: A `json_error` exception constructed from `json_errc::not_array` if the referenced node is not in the array state. 1298 | 1299 | ```cpp 1300 | constexpr explicit operator const object_type&() const&; 1301 | ``` 1302 | 1303 | 37. *Preconditions*: `*this` is valid. 1304 | 1305 | 38. *Returns*: The reference to the stored map if the referenced node is in the object state. 1306 | 1307 | 39. *Throws*: A `json_error` exception constructed from `json_errc::not_object` if the referenced node is not in the object state. 1308 | 1309 | ```cpp 1310 | constexpr explicit operator integer_type() const; 1311 | ``` 1312 | 1313 | 40. *Constraints*: `HasInteger` is `true`. 1314 | 1315 | 41. *Preconditions*: `*this` is valid. 1316 | 1317 | 42. *Returns*: The stored integer value if the referenced node is in the signed integral state. 1318 | 1319 | 43. *Throws*: A `json_error` exception constructed from `json_errc::not_integer` if the referenced node is not in the signed integral state. 1320 | 1321 | ```cpp 1322 | constexpr explicit operator uinteger_type() const; 1323 | ``` 1324 | 1325 | 44. *Constraints*: `HasUInteger` is `true`. 1326 | 1327 | 45. *Preconditions*: `*this` is valid. 1328 | 1329 | 46. *Returns*: The stored integer value if the referenced node is in the unsigned integral state. 1330 | 1331 | 47. *Throws*: A `json_error` exception constructed from `json_errc::not_uinteger` if the referenced node is not in the unsigned integral state. 1332 | 1333 | ```cpp 1334 | template 1335 | constexpr basic_const_json_slice operator[](I pos) const; 1336 | ``` 1337 | 1338 | 48. *Preconditions*: `*this` is valid. If the referenced node is in the array state, `pos` is less than the length of the stored dynamic array. 1339 | 1340 | 49. *Returns*: A `basic_const_json_slice` referencing the node at offset `pos` of the stored dynamic array if the referenced node is in the array state. 1341 | 1342 | 50. *Throws*: A `json_error` exception constructed from `json_errc::nonarray_indexing` if the referenced node is not in the array state. 1343 | 1344 | ```cpp 1345 | constexpr basic_const_json_slice operator[](const key_string_type& k) const; 1346 | template 1347 | constexpr basic_const_json_slice operator[](const KeyStrLike& k) const; 1348 | constexpr basic_const_json_slice operator[](const key_char_type* ks) const; 1349 | ``` 1350 | 1351 | 51. *Constraints*: For the template overload: 1352 | - `Object` is transparently comparable, and 1353 | - `is_convertible_v` is `false`. 1354 | 1355 | 52. *Preconditions*: `*this` is valid. 1356 | For the overload taking a `const key_char_type*`, if the referenced node is in the object state, `ks` points to a null-terminated `key_char_type` sequence. 1357 | 1358 | 53. For the overload taking a `const key_char_type*`, let `k` be a `key_string_type` value containing characters in [`ks`, `ks + count`), 1359 | where `count` is the number of characters in the null-terminated sequence. 1360 | 1361 | 54. *Returns*: If the referenced node is in the object state and an element with key `k` is found in the stored map, 1362 | a `basic_const_json_slice` referencing the mapped value of that element. 1363 | 1364 | 55. *Throws*: 1365 | - A `json_error` exception constructed from `json_errc::nonobject_indexing` if the referenced node is not in the object state. 1366 | - Otherwise, the exception thrown when looking up the key or `json_error` exception constructed from `json_errc::key_not_found` if no element with key `k` is found. 1367 | 1368 | ### Error code enumeration `json_errc` 1369 | 1370 |
1371 | namespace std {
1372 |   enum class json_errc : unspecified {
1373 |     // invalid access
1374 |     is_empty                 = see below,
1375 |     is_undefined             = see below,
1376 |     not_null                 = see below,
1377 |     not_boolean              = see below,
1378 |     not_number               = see below,
1379 |     not_integer              = see below,
1380 |     not_uinteger             = see below,
1381 |     not_string               = see below,
1382 |     not_array                = see below,
1383 |     not_object               = see below,
1384 |     nonarray_indexing        = see below,
1385 |     nonobject_indexing       = see below,
1386 |     key_not_found            = see below,
1387 | 
1388 |     // invalid modification
1389 |     not_undefined_or_null    = see below,
1390 |     not_undefined_or_boolean = see below,
1391 |     not_undefined_or_number  = see below,
1392 |     not_undefined_or_string  = see below,
1393 |   };
1394 | }
1395 | 
1396 | 1397 | 1. The enumeration type `json_errc` is used for reporting JSON-related errors. 1398 | 1399 | 2. The underlying type of `json_errc` is at least as wide as `int`. Each enumerator specified above has a distinct, nonzero value. 1400 | 1401 | ### Class `json_error` 1402 | 1403 |
1404 | namespace std {
1405 |   class json_error : public runtime_error {
1406 |   public:
1407 |     json_error(json_errc ec);
1408 |     json_errc code() const noexcept { return code_; }
1409 | 
1410 |   private:
1411 |     json_errc code_;
1412 |   };
1413 | }
1414 | 
1415 | 1416 | 1. The class `format_error` defines the exception type that reports errors from the JSON-related operations. 1417 | 1418 | ```cpp 1419 | json_error(json_errc ec); 1420 | ``` 1421 | 1422 | 2. *Postconditions*: code_ == ec. 1423 | 1424 | ## Feature-test macro 1425 | 1426 | Add a new feature-test macro to [version.syn]. 1427 | 1428 |
1429 | #define __cpp_lib_json                              202ymmL // also in <json>
1430 | 
1431 | 1432 | # Acknowledgements 1433 | 1434 | Thanks to An Jiang for the information about `polymorphic_allocator`, and the suggestions for `nulljson_t` and constructor. 1435 | 1436 | Thanks to ykiko for simplifying code using fold expressions. 1437 | 1438 | Thanks to YanzuoLiu for patiently reviewing the code and providing a graceful implementation of constructing objects. 1439 | 1440 |
1441 | {
1442 | "RFC8259": {
1443 | 	"title": "The JavaScript Object Notation (JSON) Data Interchange Format",
1444 | 	"authors": [
1445 | 		"IETF"
1446 | 	],
1447 | 	"href": "https://www.rfc-editor.org/rfc/rfc8259",
1448 | 	"date": "December 2017"
1449 | 	},
1450 | "3917": {
1451 | 	"title": "Validity of allocator and possibly polymorphic_allocator should be clarified",
1452 | 	"authors": [
1453 | 		"Daniel Krügler"
1454 | 	],
1455 | 	"href": "https://cplusplus.github.io/LWG/issue3917",
1456 | 	"date": "April 2023"
1457 | 	},
1458 | "LFTSv3": {
1459 | 	"title": "Working Draft, C++ Extensions for Library Fundamentals, Version 3",
1460 | 	"authors": [
1461 | 		"Thomas Köppe"
1462 | 	],
1463 | 	"href": "https://cplusplus.github.io/fundamentals-ts/v3.html",
1464 | 	"date": "December 17, 2022"
1465 | 	}
1466 | }
1467 | 
1468 | --------------------------------------------------------------------------------