diff --git a/doc/examples/get__PointerType.cpp b/doc/examples/get__PointerType.cpp new file mode 100644 index 00000000..e4dcec62 --- /dev/null +++ b/doc/examples/get__PointerType.cpp @@ -0,0 +1,20 @@ +#include + +using namespace nlohmann; + +int main() +{ + // create a JSON boolean + json value = 17; + + // explicitly getting pointers + auto p1 = value.get(); + auto p2 = value.get(); + auto p3 = value.get(); + auto p4 = value.get(); + auto p5 = value.get(); + + // print the pointees + std::cout << *p1 << ' ' << *p2 << ' ' << *p3 << ' ' << *p4 << '\n'; + std::cout << std::boolalpha << (p5 == nullptr) << '\n'; +} diff --git a/doc/examples/get__PointerType.output b/doc/examples/get__PointerType.output new file mode 100644 index 00000000..a15dd774 --- /dev/null +++ b/doc/examples/get__PointerType.output @@ -0,0 +1,2 @@ +17 17 17 17 +true diff --git a/doc/examples/get__ValueType_const.cpp b/doc/examples/get__ValueType_const.cpp new file mode 100644 index 00000000..3ccba548 --- /dev/null +++ b/doc/examples/get__ValueType_const.cpp @@ -0,0 +1,49 @@ +#include +#include + +using namespace nlohmann; + +int main() +{ + // 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} + }; + + // use explicit conversions + auto v1 = json_types["boolean"].get(); + auto v2 = json_types["number"]["integer"].get(); + auto v3 = json_types["number"]["integer"].get(); + auto v4 = json_types["number"]["floating-point"].get(); + auto v5 = json_types["number"]["floating-point"].get(); + auto v6 = json_types["string"].get(); + auto v7 = json_types["array"].get>(); + auto v8 = json_types.get>(); + + // print the conversion results + std::cout << v1 << '\n'; + std::cout << v2 << ' ' << v3 << '\n'; + std::cout << v4 << ' ' << v5 << '\n'; + std::cout << v6 << '\n'; + + for (auto i : v7) + { + std::cout << i << ' '; + } + std::cout << "\n\n"; + + for (auto i : v8) + { + std::cout << i.first << ": " << i.second << '\n'; + } +} diff --git a/doc/examples/get__ValueType_const.output b/doc/examples/get__ValueType_const.output new file mode 100644 index 00000000..5cd9cd3a --- /dev/null +++ b/doc/examples/get__ValueType_const.output @@ -0,0 +1,11 @@ +1 +42 42 +17.23 17 +Hello, world! +1 2 3 4 5 + +string: "Hello, world!" +number: {"floating-point":17.23,"integer":42} +null: null +boolean: true +array: [1,2,3,4,5] diff --git a/doc/examples/get_ptr.cpp b/doc/examples/get_ptr.cpp new file mode 100644 index 00000000..ddbdfc00 --- /dev/null +++ b/doc/examples/get_ptr.cpp @@ -0,0 +1,20 @@ +#include + +using namespace nlohmann; + +int main() +{ + // create a JSON boolean + json value = 17; + + // explicitly getting pointers + auto p1 = value.get_ptr(); + auto p2 = value.get_ptr(); + auto p3 = value.get_ptr(); + auto p4 = value.get_ptr(); + auto p5 = value.get_ptr(); + + // print the pointees + std::cout << *p1 << ' ' << *p2 << ' ' << *p3 << ' ' << *p4 << '\n'; + std::cout << std::boolalpha << (p5 == nullptr) << '\n'; +} diff --git a/doc/examples/get_ptr.output b/doc/examples/get_ptr.output new file mode 100644 index 00000000..a15dd774 --- /dev/null +++ b/doc/examples/get_ptr.output @@ -0,0 +1,2 @@ +17 17 17 17 +true diff --git a/doc/examples/operator__ValueType.cpp b/doc/examples/operator__ValueType.cpp new file mode 100644 index 00000000..962b4bf6 --- /dev/null +++ b/doc/examples/operator__ValueType.cpp @@ -0,0 +1,49 @@ +#include +#include + +using namespace nlohmann; + +int main() +{ + // 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} + }; + + // use implicit conversions + bool v1 = json_types["boolean"]; + int v2 = json_types["number"]["integer"]; + short v3 = json_types["number"]["integer"]; + float v4 = json_types["number"]["floating-point"]; + int v5 = json_types["number"]["floating-point"]; + std::string v6 = json_types["string"]; + std::vector v7 = json_types["array"]; + std::unordered_map v8 = json_types; + + // print the conversion results + std::cout << v1 << '\n'; + std::cout << v2 << ' ' << v3 << '\n'; + std::cout << v4 << ' ' << v5 << '\n'; + std::cout << v6 << '\n'; + + for (auto i : v7) + { + std::cout << i << ' '; + } + std::cout << "\n\n"; + + for (auto i : v8) + { + std::cout << i.first << ": " << i.second << '\n'; + } +} diff --git a/doc/examples/operator__ValueType.output b/doc/examples/operator__ValueType.output new file mode 100644 index 00000000..5cd9cd3a --- /dev/null +++ b/doc/examples/operator__ValueType.output @@ -0,0 +1,11 @@ +1 +42 42 +17.23 17 +Hello, world! +1 2 3 4 5 + +string: "Hello, world!" +number: {"floating-point":17.23,"integer":42} +null: null +boolean: true +array: [1,2,3,4,5] diff --git a/src/json.hpp b/src/json.hpp index ed4d24fd..d2f62209 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1787,24 +1787,192 @@ class basic_json } } + /// get a pointer to the value (object) + const object_t* get_impl_ptr(object_t*) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + const array_t* get_impl_ptr(array_t*) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + const string_t* get_impl_ptr(string_t*) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + const boolean_t* get_impl_ptr(boolean_t*) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + const number_integer_t* get_impl_ptr(number_integer_t*) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (floating-point number) + const number_float_t* get_impl_ptr(number_float_t*) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + public: /// @name value access /// @{ - /// get a value (explicit) - // - template - T get() const + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows serveral conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + assiciative containers such as `std::unordered_map`.,get__ValueType_const} + + @internal + The idea of using a casted null pointer to choose the correct + implementation is from . + @endinternal + + @sa @ref operator ValueType() const for implicit conversion + @sa @ref get() for pointer-member access + */ + template::value + , int>::type = 0> + ValueType get() const { - return get_impl(static_cast(nullptr)); + return get_impl(static_cast(nullptr)); } - /// get a value (implicit) - template - operator T() const + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref + number_float_t. + + @return pointer to the internally stored JSON value if the requested pointer + type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + */ + template::value + , int>::type = 0> + PointerType get() const noexcept { - return get(); + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (implicit) + + Implict pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref + number_float_t. + + @return pointer to the internally stored JSON value if the requested pointer + type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + */ + template::value + , int>::type = 0> + PointerType get_ptr() const noexcept + { + // get_impl_ptr will only work with non-const and non-volatile pointer + // types. Therefore, we case away all cv properties to be able to + // select the correct function. The cv properties will then be added + // again by the const const cast to PointerType. + return const_cast(get_impl_ptr( + static_cast::type>::type>::type> + (nullptr))); + } + + /*! + @brief get a value (implicit) + + Implict type conversion between the JSON value and a compatible value. The + call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON, thrown by @ref get() const + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows serveral conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + assiciative containers such as `std::unordered_map`.,operator__ValueType} + */ + template::value + , int>::type = 0> + operator ValueType() const + { + // delegate the call to get<>() const + return get(); } /// @} diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 4c574e6f..fed27c1d 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1787,24 +1787,192 @@ class basic_json } } + /// get a pointer to the value (object) + const object_t* get_impl_ptr(object_t*) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + const array_t* get_impl_ptr(array_t*) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + const string_t* get_impl_ptr(string_t*) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + const boolean_t* get_impl_ptr(boolean_t*) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + const number_integer_t* get_impl_ptr(number_integer_t*) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (floating-point number) + const number_float_t* get_impl_ptr(number_float_t*) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + public: /// @name value access /// @{ - /// get a value (explicit) - // - template - T get() const + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows serveral conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + assiciative containers such as `std::unordered_map`.,get__ValueType_const} + + @internal + The idea of using a casted null pointer to choose the correct + implementation is from . + @endinternal + + @sa @ref operator ValueType() const for implicit conversion + @sa @ref get() for pointer-member access + */ + template::value + , int>::type = 0> + ValueType get() const { - return get_impl(static_cast(nullptr)); + return get_impl(static_cast(nullptr)); } - /// get a value (implicit) - template - operator T() const + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref + number_float_t. + + @return pointer to the internally stored JSON value if the requested pointer + type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + */ + template::value + , int>::type = 0> + PointerType get() const noexcept { - return get(); + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (implicit) + + Implict pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref + number_float_t. + + @return pointer to the internally stored JSON value if the requested pointer + type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + */ + template::value + , int>::type = 0> + PointerType get_ptr() const noexcept + { + // get_impl_ptr will only work with non-const and non-volatile pointer + // types. Therefore, we case away all cv properties to be able to + // select the correct function. The cv properties will then be added + // again by the const const cast to PointerType. + return const_cast(get_impl_ptr( + static_cast::type>::type>::type> + (nullptr))); + } + + /*! + @brief get a value (implicit) + + Implict type conversion between the JSON value and a compatible value. The + call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON, thrown by @ref get() const + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows serveral conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + assiciative containers such as `std::unordered_map`.,operator__ValueType} + */ + template::value + , int>::type = 0> + operator ValueType() const + { + // delegate the call to get<>() const + return get(); } /// @} diff --git a/test/unit.cpp b/test/unit.cpp index c8074b34..b0d20ef4 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -2434,6 +2434,186 @@ TEST_CASE("value conversion") } } +TEST_CASE("pointer 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("pointer access to object_t") + { + using test_type = json::object_t; + json value = {{"one", 1}, {"two", 2}}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to array_t") + { + using test_type = json::array_t; + json value = {1, 2, 3, 4}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to string_t") + { + using test_type = json::string_t; + json value = "hello"; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to boolean_t") + { + using test_type = json::boolean_t; + json value = false; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_integer_t") + { + using test_type = json::number_integer_t; + json value = 23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_float_t") + { + using test_type = json::number_float_t; + json value = 42.23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == Approx(value.get())); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == Approx(value.get())); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == Approx(value.get())); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + } +} + TEST_CASE("element access") { SECTION("array")