From fc1fb583de222caecdb956c623765b6a1a047937 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 13 Feb 2024 11:05:37 +0200 Subject: Extend json_value C++ interface --- libbuild2/json.cxx | 62 +++++++++++++++++++++++--- libbuild2/json.hxx | 69 ++++++++++++++++++++++++----- libbuild2/json.ixx | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+), 18 deletions(-) diff --git a/libbuild2/json.cxx b/libbuild2/json.cxx index f368679..6295b59 100644 --- a/libbuild2/json.cxx +++ b/libbuild2/json.cxx @@ -61,6 +61,17 @@ namespace build2 const json_value null_json_value (json_type::null); [[noreturn]] void + json_as_throw (json_type t, json_type e) + { + string m; + m = "expected "; + m += to_string (e, true); + m += " instead of "; + m += to_string (t, true); + throw invalid_argument (move (m)); + } + + [[noreturn]] static void at_throw (json_type t, json_type e, bool index) { string m; @@ -68,16 +79,16 @@ namespace build2 if (t != e && t != json_type::null) { m = "expected "; - m += to_string (e); + m += to_string (e, true); m += " instead of "; - m += to_string (t); + m += to_string (t, true); throw invalid_argument (move (m)); } else { m = index ? "index" : "name"; m += " out of range in "; - m += to_string (e); + m += to_string (e, true); throw std::out_of_range (move (m)); } } @@ -106,6 +117,7 @@ namespace build2 at_throw (type, json_type::array, true); } +#if 0 const json_value& json_value:: operator[] (size_t index) const { @@ -145,6 +157,7 @@ namespace build2 at_throw (type, json_type::array, true); } +#endif const json_value& json_value:: at (const char* name) const @@ -182,6 +195,40 @@ namespace build2 at_throw (type, json_type::object, false); } + const json_value* json_value:: + find (const char* name) const + { + if (type == json_type::object) + { + auto i (find_if (object.begin (), object.end (), + [name] (const json_member& m) + { + return m.name == name; + })); + return i != object.end () ? &i->value : nullptr; + } + + at_throw (type, json_type::object, false); + } + + json_value* json_value:: + find (const char* name) + { + if (type == json_type::object) + { + auto i (find_if (object.begin (), object.end (), + [name] (const json_member& m) + { + return m.name == name; + })); + + return i != object.end () ? &i->value : nullptr; + } + + at_throw (type, json_type::object, false); + } + +#if 0 const json_value& json_value:: operator[] (const char* name) const { @@ -229,6 +276,7 @@ namespace build2 at_throw (type, json_type::object, false); } +#endif int json_value:: compare (const json_value& v) const noexcept @@ -683,8 +731,8 @@ namespace build2 p.line (), p.column (), p.position (), - string_type ("expected ") + to_string (*et) + " instead of " + - to_string (t)); + string_type ("expected ") + to_string (*et, true) + " instead of " + + to_string (t, true)); } switch (t) @@ -782,8 +830,8 @@ namespace build2 throw invalid_json_output ( nullopt, invalid_json_output::error_code::invalid_value, - string_type ("expected ") + to_string (*et) + " instead of " + - to_string (type)); + string_type ("expected ") + to_string (*et, true) + " instead of " + + to_string (type, true)); } switch (type) diff --git a/libbuild2/json.hxx b/libbuild2/json.hxx index 1f2694d..538c87d 100644 --- a/libbuild2/json.hxx +++ b/libbuild2/json.hxx @@ -87,6 +87,8 @@ namespace build2 json_type type; + // Unchecked value access. + // union { bool boolean; @@ -97,6 +99,32 @@ namespace build2 object_type object; }; + // Checked value access. + // + // If the type matches, return the corresponding member of the union. + // Otherwise throw std::invalid_argument. + // + bool as_bool () const; + bool& as_bool (); + + int64_t as_int64 () const; + int64_t& as_int64 (); + + uint64_t as_uint64 () const; + uint64_t& as_uint64 (); + + const string_type& as_string () const; + string_type& as_string (); + + const array_type& as_array () const; + array_type& as_array (); + + const object_type& as_object () const; + object_type& as_object (); + + + // Construction. + // explicit json_value (json_type = json_type::null) noexcept; @@ -161,7 +189,7 @@ namespace build2 // position (filling any missing elements in between with nulls) and // returns that. All three functions throw std::invalid_argument if the // value is not an array or null with null treated as (missing) array - // rather than wrong value type (and with at() functons throwing + // rather than wrong value type (and with at() functions throwing // out_of_range in this case). // // Note that non-const operator[] will not only insert a new element but @@ -174,8 +202,8 @@ namespace build2 // can lead to inefficiencies or even undesirable semantics during // otherwise read-only access of a non-const object due to the potential // insertion of null values for missing array elements. As a result, it's - // recommended to alwas use a const reference for read-only access (or use - // the at() interface if this is deemed too easy to forget). + // recommended to always use a const reference for read-only access (or + // use the at() interface if this is deemed too easy to forget). // const json_value& at (size_t) const; @@ -183,22 +211,25 @@ namespace build2 json_value& at (size_t); +#if 0 const json_value& operator[] (size_t) const; json_value& operator[] (size_t); +#endif // Object member access. // // If a member with the specified name is not found in the object, the - // at() functions throw std::out_of_range, the const operator[] returns - // null_json_value, and the non-const operator[] adds a new member with - // the specified name and null value and returns that value. All three - // functions throw std::invalid_argument if the value is not an array or - // null with null treated as (missing) object rather than wrong value type - // (and with at() functons throwing out_of_range in this case). + // at() functions throw std::out_of_range, the find() function returns + // NULL, the const operator[] returns null_json_value, and the non-const + // operator[] adds a new member with the specified name and null value and + // returns that value. All three functions throw std::invalid_argument if + // the value is not an object or null with null treated as (missing) + // object rather than wrong value type (and with at() functions throwing + // out_of_range in this case). // // Note that non-const operator[] will not only insert a new member but // will also turn the value it is called upon into object if it is null. @@ -210,8 +241,8 @@ namespace build2 // can lead to inefficiencies or even undesirable semantics during // otherwise read-only access of a non-const object due to the potential // insertion of null values for missing object members. As a result, it's - // recommended to alwas use a const reference for read-only access (or use - // the at() interface if this is deemed too easy to forget). + // recommended to always use a const reference for read-only access (or + // use the at() interface if this is deemed too easy to forget). // const json_value& at (const char*) const; @@ -219,11 +250,19 @@ namespace build2 json_value& at (const char*); + const json_value* + find (const char*) const; + + json_value* + find (const char*); + +#if 0 const json_value& operator[] (const char*) const; json_value& operator[] (const char*); +#endif const json_value& at (const string_type&) const; @@ -231,11 +270,19 @@ namespace build2 json_value& at (const string_type&); + const json_value* + find (const string_type&) const; + + json_value* + find (const string_type&); + +#if 0 const json_value& operator[] (const string_type&) const; json_value& operator[] (const string_type&); +#endif // Note that the moved-from value becomes JSON null value. // diff --git a/libbuild2/json.ixx b/libbuild2/json.ixx index c2b8845..76cd00a 100644 --- a/libbuild2/json.ixx +++ b/libbuild2/json.ixx @@ -3,6 +3,119 @@ namespace build2 { + [[noreturn]] LIBBUILD2_SYMEXPORT void + json_as_throw (json_type actual, json_type expected); + + inline bool json_value:: + as_bool () const + { + if (type == json_type::boolean) + return boolean; + + json_as_throw (type, json_type::boolean); + } + + inline bool& json_value:: + as_bool () + { + if (type == json_type::boolean) + return boolean; + + json_as_throw (type, json_type::boolean); + } + + inline int64_t json_value:: + as_int64 () const + { + if (type == json_type::signed_number) + return signed_number; + + json_as_throw (type, json_type::signed_number); + } + + inline int64_t& json_value:: + as_int64 () + { + if (type == json_type::signed_number) + return signed_number; + + json_as_throw (type, json_type::signed_number); + } + + inline uint64_t json_value:: + as_uint64 () const + { + if (type == json_type::unsigned_number || + type == json_type::hexadecimal_number) + return unsigned_number; + + json_as_throw (type, json_type::unsigned_number); + } + + inline uint64_t& json_value:: + as_uint64 () + { + if (type == json_type::unsigned_number || + type == json_type::hexadecimal_number) + return unsigned_number; + + json_as_throw (type, json_type::unsigned_number); + } + + inline const string& json_value:: + as_string () const + { + if (type == json_type::string) + return string; + + json_as_throw (type, json_type::string); + } + + inline string& json_value:: + as_string () + { + if (type == json_type::string) + return string; + + json_as_throw (type, json_type::string); + } + + inline const json_value::array_type& json_value:: + as_array () const + { + if (type == json_type::array) + return array; + + json_as_throw (type, json_type::array); + } + + inline json_value::array_type& json_value:: + as_array () + { + if (type == json_type::array) + return array; + + json_as_throw (type, json_type::array); + } + + inline const json_value::object_type& json_value:: + as_object () const + { + if (type == json_type::object) + return object; + + json_as_throw (type, json_type::object); + } + + inline json_value::object_type& json_value:: + as_object () + { + if (type == json_type::object) + return object; + + json_as_throw (type, json_type::object); + } + inline json_value:: ~json_value () noexcept { @@ -81,6 +194,19 @@ namespace build2 return at (n.c_str ()); } + inline const json_value* json_value:: + find (const string_type& n) const + { + return find (n.c_str ()); + } + + inline json_value* json_value:: + find (const string_type& n) + { + return find (n.c_str ()); + } + +#if 0 inline const json_value& json_value:: operator[] (const string_type& n) const { @@ -92,6 +218,7 @@ namespace build2 { return operator[] (n.c_str ()); } +#endif inline json_value:: json_value (json_value&& v) noexcept -- cgit v1.1