@@ -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