diff --git a/doc/examples/get_ref.cpp b/doc/examples/get_ref.cpp new file mode 100644 index 000000000..a8a868532 --- /dev/null +++ b/doc/examples/get_ref.cpp @@ -0,0 +1,26 @@ +#include + +using namespace nlohmann; + +int main() +{ + // create a JSON number + json value = 17; + + // explicitly getting references + auto r1 = value.get_ref(); + auto r2 = value.get_ref(); + + // print the values + std::cout << r1 << ' ' << r2 << '\n'; + + // incompatible type throws exception + try + { + auto r3 = value.get_ref(); + } + catch(std::domain_error& ex) + { + std::cout << ex.what() << '\n'; + } +} diff --git a/src/json.hpp b/src/json.hpp index a91edba4f..81c02857f 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -2416,6 +2416,19 @@ class basic_json return is_number_float() ? &m_value.number_float : nullptr; } + /// helper function to implement get_ref without code duplication + /// for const and non-const overloads + /// ThisType will be deduced as 'basic_jason' or 'const basic_json' + template + static ReferenceType get_ref_impl(ThisType& obj) + { + using PointerType = typename std::add_pointer::type; + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr(); + if (ptr) return *ptr; + throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + obj.type_name()); + } + public: /// @name value access @@ -2563,6 +2576,52 @@ class basic_json return get_impl_ptr(static_cast(nullptr)); } + /*! + @brief get a reference value (implicit) + + Implict reference access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref + number_float_t. + + @return reference to the internally stored JSON value if the requested reference + type @a ReferenceType fits to the JSON value; throws std::domain_error otherwise + + @throw std::domain_error in case passed type @a ReferenceType is incompatible + with the stored JSON value + + @complexity Constant. + */ + template::value + , int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template::value + and std::is_const< typename std::remove_reference::type >::value + , int>::type = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + /*! @brief get a value (implicit) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index a733f0979..0838b234d 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -2416,6 +2416,19 @@ class basic_json return is_number_float() ? &m_value.number_float : nullptr; } + /// helper function to implement get_ref without code duplication + /// for const and non-const overloads + /// ThisType will be deduced as 'basic_jason' or 'const basic_json' + template + static ReferenceType get_ref_impl(ThisType& obj) + { + using PointerType = typename std::add_pointer::type; + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr(); + if (ptr) return *ptr; + throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + obj.type_name()); + } + public: /// @name value access @@ -2563,6 +2576,52 @@ class basic_json return get_impl_ptr(static_cast(nullptr)); } + /*! + @brief get a reference value (implicit) + + Implict reference access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref + number_float_t. + + @return reference to the internally stored JSON value if the requested reference + type @a ReferenceType fits to the JSON value; throws std::domain_error otherwise + + @throw std::domain_error in case passed type @a ReferenceType is incompatible + with the stored JSON value + + @complexity Constant. + */ + template::value + , int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template::value + and std::is_const< typename std::remove_reference::type >::value + , int>::type = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + /*! @brief get a value (implicit) diff --git a/test/unit.cpp b/test/unit.cpp index ebf3fb2a3..81b341f69 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -2604,6 +2604,22 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const object_t") + { + using test_type = json::object_t; + const json value = {{"one", 1}, {"two", 2}}; + + // this should not compile + // test_type* p1 = value.get_ptr(); + + // check if pointers are returned correctly + const test_type* p2 = value.get_ptr(); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p2 == p3); + } + SECTION("pointer access to array_t") { using test_type = json::array_t; @@ -2740,6 +2756,176 @@ TEST_CASE("pointer access") } } +TEST_CASE("reference access") +{ + // create a JSON value with different types + json json_types = + { + {"boolean", true}, + { + "number", { + {"integer", 42}, + {"floating-point", 17.23} + } + }, + {"string", "Hello, world!"}, + {"array", {1, 2, 3, 4, 5}}, + {"null", nullptr} + }; + + SECTION("reference access to object_t") + { + using test_type = json::object_t; + json value = {{"one", 1}, {"two", 2}}; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("const reference access to const object_t") + { + using test_type = json::object_t; + const json value = {{"one", 1}, {"two", 2}}; + + // this should not compile + // test_type& p1 = value.get_ref(); + + // check if references are returned correctly + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + } + + SECTION("reference access to array_t") + { + using test_type = json::array_t; + json value = {1, 2, 3, 4}; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to string_t") + { + using test_type = json::string_t; + json value = "hello"; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to boolean_t") + { + using test_type = json::boolean_t; + json value = false; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to number_integer_t") + { + using test_type = json::number_integer_t; + json value = 23; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to number_float_t") + { + using test_type = json::number_float_t; + json value = 42.23; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + } +} + TEST_CASE("element access") { SECTION("array")