@@ -22,6 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222SOFTWARE.
2323*/
2424
25+ // Important note: the types in this file are only intended for compile-time construction
26+ // but consteval doesn't exist in C++17
27+
2528#ifndef CONSTEXPR_JSON_HPP_INCLUDED
2629#define CONSTEXPR_JSON_HPP_INCLUDED
2730
@@ -30,7 +33,6 @@ SOFTWARE.
3033#include < cstdint>
3134#include < stdexcept>
3235#include < string_view>
33- #include < variant>
3436
3537namespace constexpr_json {
3638template <typename First, typename Second> struct pair
@@ -58,23 +60,138 @@ template<typename T> struct span
5860 const T *end_;
5961};
6062
61- struct json ;
63+ template <typename CharType> struct basic_json ;
64+ template <typename CharType> using basic_array_t = span<basic_json<CharType>>;
65+ template <typename CharType> using basic_value_pair_t = pair<std::basic_string_view<CharType>, basic_json<CharType>>;
66+ template <typename CharType> using basic_object_t = span<basic_value_pair_t <CharType>>;
6267
63- using array_t = span<json>;
64- using value_pair_t = pair<std::string_view, json>;
65- using object_t = span<value_pair_t >;
6668using binary_t = span<std::uint8_t >;
6769
68- using data_t = std::
69- variant<std::monostate, bool , binary_t , array_t , object_t , std::int64_t , std::uint64_t , double , std::string_view>;
70+ template <typename CharType> struct data_variant
71+ {
72+ struct monostate
73+ {
74+ };
75+
76+ union value_t {
77+ monostate empty_;
78+ bool bool_;
79+ binary_t binary_;
80+ basic_array_t <CharType> array_;
81+ basic_object_t <CharType> object_;
82+ std::int64_t int64_t_;
83+ std::uint64_t uint64_t_;
84+ double double_;
85+ std::basic_string_view<CharType> string_view_;
86+
87+ constexpr explicit value_t () : empty_{} {}
88+ constexpr explicit value_t (monostate) : value_t () {}
89+ constexpr explicit value_t (bool b) : bool_{ b } {}
90+ constexpr explicit value_t (binary_t b) : binary_{ b } {}
91+ constexpr explicit value_t (basic_array_t <CharType> a) : array_{ a } {}
92+ constexpr explicit value_t (basic_object_t <CharType> o) : object_{ o } {}
93+ constexpr explicit value_t (std::int64_t i) : int64_t_{ i } {}
94+ constexpr explicit value_t (std::uint64_t i) : uint64_t_{ i } {}
95+ constexpr explicit value_t (double d) : double_{ d } {}
96+ constexpr explicit value_t (std::basic_string_view<CharType> s) : string_view_{ s } {}
97+ };
98+
99+ enum struct selected_type { empty, boolean, binary, array, object, integer, uinteger, floating_point, string };
100+
101+ value_t value{ monostate{} };
102+ selected_type selected{ selected_type::empty };
103+
104+ // letting these be implicit measurably improves construction performance
105+
106+ constexpr data_variant () = default;
107+ // cppcheck-suppress noExplicitConstructor
108+ constexpr data_variant (monostate) : data_variant() {}
109+ // cppcheck-suppress noExplicitConstructor
110+ constexpr data_variant (bool b) : value{ b }, selected{ selected_type::boolean } {}
111+ // cppcheck-suppress noExplicitConstructor
112+ constexpr data_variant (basic_array_t <CharType> a) : value{ a }, selected{ selected_type::array } {}
113+ // cppcheck-suppress noExplicitConstructor
114+ constexpr data_variant (basic_object_t <CharType> o) : value{ o }, selected{ selected_type::object } {}
115+ // cppcheck-suppress noExplicitConstructor
116+ constexpr data_variant (std::int64_t i) : value{ i }, selected{ selected_type::integer } {}
117+ // cppcheck-suppress noExplicitConstructor
118+ constexpr data_variant (std::uint64_t i) : value{ i }, selected{ selected_type::uinteger } {}
119+ // cppcheck-suppress noExplicitConstructor
120+ constexpr data_variant (double d) : value{ d }, selected{ selected_type::floating_point } {}
121+ // cppcheck-suppress noExplicitConstructor
122+ constexpr data_variant (std::basic_string_view<CharType> s) : value{ s }, selected{ selected_type::string } {}
123+
124+ [[nodiscard]] constexpr const bool *get_if_boolean () const noexcept
125+ {
126+ if (selected == selected_type::boolean) {
127+ return &value.bool_ ;
128+ } else {
129+ return nullptr ;
130+ }
131+ }
132+
133+ [[nodiscard]] constexpr const basic_array_t <CharType> *get_if_array () const noexcept
134+ {
135+ if (selected == selected_type::array) {
136+ return &value.array_ ;
137+ } else {
138+ return nullptr ;
139+ }
140+ }
141+ [[nodiscard]] constexpr const basic_object_t <CharType> *get_if_object () const noexcept
142+ {
143+ if (selected == selected_type::object) {
144+ return &value.object_ ;
145+ } else {
146+ return nullptr ;
147+ }
148+ }
149+ [[nodiscard]] constexpr const std::int64_t *get_if_integer () const noexcept
150+ {
151+ if (selected == selected_type::integer) {
152+ return &value.int64_t_ ;
153+ } else {
154+ return nullptr ;
155+ }
156+ }
157+ [[nodiscard]] constexpr const std::uint64_t *get_if_uinteger () const noexcept
158+ {
159+ if (selected == selected_type::uinteger) {
160+ return &value.uint64_t_ ;
161+ } else {
162+ return nullptr ;
163+ }
164+ }
165+
166+ [[nodiscard]] constexpr const double *get_if_floating_point () const noexcept
167+ {
168+ if (selected == selected_type::floating_point) {
169+ return &value.double_ ;
170+ } else {
171+ return nullptr ;
172+ }
173+ }
174+
175+ [[nodiscard]] constexpr const std::basic_string_view<CharType> *get_if_string () const noexcept
176+ {
177+ if (selected == selected_type::string) {
178+ return &value.string_view_ ;
179+ } else {
180+ return nullptr ;
181+ }
182+ }
183+ };
70184
71- struct json
185+ template < typename CharType> struct basic_json
72186{
187+ using data_t = data_variant<CharType>;
188+
73189 struct iterator
74190 {
75- constexpr explicit iterator (const json &value, std::size_t index = 0 ) : parent_value_(&value), index_{ index } {}
191+ constexpr explicit iterator (const basic_json &value, std::size_t index = 0 ) : parent_value_(&value), index_{ index }
192+ {}
76193
77- constexpr const json &operator *() const
194+ constexpr const basic_json &operator *() const
78195 {
79196 if (parent_value_->is_array ()) {
80197 return (*parent_value_)[index_];
@@ -85,14 +202,14 @@ struct json
85202 }
86203 }
87204
88- constexpr const json *operator ->() const { return &(*(*this )); }
205+ constexpr const basic_json *operator ->() const { return &(*(*this )); }
89206
90207 constexpr std::size_t index () const { return index_; }
91208
92- constexpr const json &value () const { return *(*this ); }
209+ constexpr const basic_json &value () const { return *(*this ); }
93210
94211
95- constexpr std::string_view key () const
212+ constexpr std::basic_string_view<CharType> key () const
96213 {
97214 if (parent_value_->is_object ()) {
98215 return std::next (parent_value_->object_data ().begin (), static_cast <std::ptrdiff_t >(index_))->first ;
@@ -153,7 +270,7 @@ struct json
153270 return *this ;
154271 }
155272
156- const json *parent_value_{ nullptr };
273+ const basic_json *parent_value_{ nullptr };
157274 std::size_t index_{ 0 };
158275 };
159276
@@ -170,14 +287,14 @@ struct json
170287 constexpr std::size_t size () const noexcept
171288 {
172289 if (is_null ()) { return 0 ; }
173- if (is_object ()) { return std::get_if< object_t >(& data)->size (); }
290+ if (is_object ()) { return data. get_if_object ( )->size (); }
174291
175- if (is_array ()) { return std::get_if< array_t >(& data)->size (); }
292+ if (is_array ()) { return data. get_if_array ( )->size (); }
176293
177294 return 1 ;
178295 }
179296
180- constexpr const json &operator [](const std::size_t idx) const
297+ constexpr const basic_json &operator [](const std::size_t idx) const
181298 {
182299 if (const auto &children = array_data (); idx < children.size ()) {
183300 return *std::next (children.begin (), static_cast <std::ptrdiff_t >(idx));
@@ -195,7 +312,7 @@ struct json
195312 return end ();
196313 }
197314
198- constexpr const json &operator [](const std::string_view key) const
315+ constexpr const basic_json &operator [](const std::string_view key) const
199316 {
200317 const auto &children = object_data ();
201318
@@ -221,82 +338,80 @@ struct json
221338 }
222339 }
223340
224- constexpr const array_t &array_data () const
341+ constexpr const auto &array_data () const
225342 {
226- if (const auto *result = std::get_if< array_t >(& data); result != nullptr ) {
343+ if (const auto *result = data. get_if_array ( ); result != nullptr ) {
227344 return *result;
228345 } else {
229346 throw std::runtime_error (" value is not an array type" );
230347 }
231348 }
232349
233- constexpr const object_t &object_data () const
350+ constexpr const auto &object_data () const
234351 {
235- if (const auto *result = std::get_if< object_t >(& data); result != nullptr ) {
352+ if (const auto *result = data. get_if_object ( ); result != nullptr ) {
236353 return *result;
237354 } else {
238355 throw std::runtime_error (" value is not an object type" );
239356 }
240357 }
241358
242- constexpr static json object () { return json{ object_t {} }; }
243-
244- constexpr static json array () { return json{ array_t {} }; }
245-
359+ constexpr static basic_json object () { return basic_json{ data_t { basic_object_t <CharType>{} } }; }
360+ constexpr static basic_json array () { return basic_json{ data_t { basic_array_t <CharType>{} } }; }
246361
247362 template <typename Type> constexpr Type get () const
248363 {
249364 if constexpr (std::is_same_v<Type, std::uint64_t > || std::is_same_v<Type, std::int64_t >) {
250- if (const auto *uint_value = std::get_if<std:: uint64_t >(& data); uint_value != nullptr ) {
365+ if (const auto *uint_value = data. get_if_uinteger ( ); uint_value != nullptr ) {
251366 return Type (*uint_value);
252- } else if (const auto *value = std::get_if<std:: int64_t >(& data); value != nullptr ) {
367+ } else if (const auto *value = data. get_if_integer ( ); value != nullptr ) {
253368 return Type (*value);
254369 }
255370 } else if constexpr (std::is_same_v<Type, double >) {
256- if (const auto *value = std::get_if< double >(& data); value != nullptr ) { return *value; }
371+ if (const auto *value = data. get_if_floating_point ( ); value != nullptr ) { return *value; }
257372 } else if constexpr (std::is_same_v<Type, std::string_view>) {
258- if (const auto *value = std::get_if<std::string_view>(& data); value != nullptr ) { return *value; }
373+ if (const auto *value = data. get_if_string ( ); value != nullptr ) { return *value; }
259374 } else if constexpr (std::is_same_v<Type, bool >) {
260- if (const auto *value = std::get_if< bool >(& data); value != nullptr ) { return *value; }
375+ if (const auto *value = data. get_if_boolean ( ); value != nullptr ) { return *value; }
261376 } else {
262377 throw std::runtime_error (" Unexpected type for get()" );
263378 }
264379
265380 throw std::runtime_error (" Incorrect type for get()" );
266381 }
267382
268- constexpr bool is_object () const noexcept { return std::holds_alternative< object_t >(data) ; }
383+ constexpr bool is_object () const noexcept { return data. selected == data_t ::selected_type::object ; }
269384
270- constexpr bool is_array () const noexcept { return std::holds_alternative< array_t >(data) ; }
385+ constexpr bool is_array () const noexcept { return data. selected == data_t ::selected_type::array ; }
271386
272- constexpr bool is_string () const noexcept { return std::holds_alternative<std::string_view>(data) ; }
387+ constexpr bool is_string () const noexcept { return data. selected == data_t ::selected_type::string ; }
273388
274- constexpr bool is_boolean () const noexcept { return std::holds_alternative< bool >(data) ; }
389+ constexpr bool is_boolean () const noexcept { return data. selected == data_t ::selected_type::boolean ; }
275390
276391 constexpr bool is_structured () const noexcept { return is_object () || is_array (); }
277392
278393 constexpr bool is_number () const noexcept { return is_number_integer () || is_number_float (); }
279394
280395 constexpr double as_number_float () const
281396 {
282- if (const double *value = std::get_if< double >(& data); value != nullptr ) {
397+ if (const double *value = data. get_if_floating_point ( ); value != nullptr ) {
283398 return *value;
284399 } else {
285400 throw std::runtime_error (" Not a float type" );
286401 }
287402 }
288403 constexpr double as_boolean () const
289404 {
290- if (const bool *value = std::get_if< bool >(& data); value != nullptr ) {
405+ if (const bool *value = data. get_if_boolean ( ); value != nullptr ) {
291406 return *value;
292407 } else {
293408 throw std::runtime_error (" Not a boolean type" );
294409 }
295410 }
296411
297- constexpr std::string_view as_string () const
412+ constexpr auto as_string () const
298413 {
299- if (const auto *value = std::get_if<std::string_view>(& data); value != nullptr ) {
414+ if (const auto *value = data. get_if_string ( ); value != nullptr ) {
300415 return *value;
301416 } else {
302417 throw std::runtime_error (" Not a string type" );
@@ -307,15 +422,15 @@ struct json
307422
308423 constexpr bool is_number_integer () const noexcept { return is_number_signed () || is_number_unsigned (); }
309424
310- constexpr bool is_number_signed () const noexcept { return std::holds_alternative<std:: int64_t >(data) ; }
425+ constexpr bool is_number_signed () const noexcept { return data. selected == data_t ::selected_type::integer ; }
311426
312- constexpr bool is_number_unsigned () const noexcept { return std::holds_alternative<std:: uint64_t >(data) ; }
427+ constexpr bool is_number_unsigned () const noexcept { return data. selected == data_t ::selected_type::uinteger ; }
313428
314- constexpr bool is_number_float () const noexcept { return std::holds_alternative< double >(data) ; }
429+ constexpr bool is_number_float () const noexcept { return data. selected == data_t ::selected_type::floating_point ; }
315430
316- constexpr bool is_null () const noexcept { return std::holds_alternative<std::monostate>(data) ; }
431+ constexpr bool is_null () const noexcept { return data. selected == data_t ::selected_type::empty ; }
317432
318- constexpr bool is_binary () const noexcept { return std::holds_alternative< binary_t >(data) ; }
433+ constexpr bool is_binary () const noexcept { return data. selected == data_t ::selected_type::binary ; }
319434
320435 constexpr bool is_primitive () const noexcept
321436 {
@@ -325,6 +440,10 @@ struct json
325440 data_t data;
326441};
327442
443+ using json = basic_json<char >;
444+ using object_t = basic_object_t <char >;
445+ using value_pair_t = basic_value_pair_t <char >;
446+ using array_t = basic_array_t <char >;
328447}// namespace constexpr_json
329448
330449#endif
0 commit comments