diff --git a/docs/mkdocs/docs/api/basic_json/as_base_class.md b/docs/mkdocs/docs/api/basic_json/as_base_class.md new file mode 100644 index 000000000..769707140 --- /dev/null +++ b/docs/mkdocs/docs/api/basic_json/as_base_class.md @@ -0,0 +1,53 @@ +# nlohmann::basic_json::as_base_class + +```cpp +json_base_class_t& as_base_class() noexcept; +const json_base_class_t& as_base_class() const noexcept; +``` + +Returns a reference to this object as its custom base class [`json_base_class_t`](json_base_class_t.md). No copy is +made. + +Since `basic_json` derives from `json_base_class_t`, a member of `basic_json` hides any member of the custom base class +with the same name. This function makes such hidden members accessible again. + +## Return value + +reference to this object as [`json_base_class_t`](json_base_class_t.md) + +## Exception safety + +No-throw guarantee: this function never throws exceptions. + +## Complexity + +Constant. + +## Notes + +The function is equivalent to `static_cast(j)` (or `static_cast(j)`). + +## Examples + +??? example + + The example shows how to use `as_base_class` to access members of the custom base class that are hidden by members + of `basic_json`. + + ```cpp + --8<-- "examples/as_base_class.cpp" + ``` + + Output: + + ```json + --8<-- "examples/as_base_class.output" + ``` + +## See also + +- [json_base_class_t](json_base_class_t.md) - type of the custom base class + +## Version history + +- Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md index c5556ea3b..54ddc1909 100644 --- a/docs/mkdocs/docs/api/basic_json/index.md +++ b/docs/mkdocs/docs/api/basic_json/index.md @@ -200,6 +200,7 @@ Direct access to the stored value of a JSON value. - [**get_ref**](get_ref.md) - get a reference value - [**operator ValueType**](operator_ValueType.md) - get a value - [**get_binary**](get_binary.md) - get a binary value +- [**as_base_class**](as_base_class.md) - access the custom base class ### Element access diff --git a/docs/mkdocs/docs/api/basic_json/json_base_class_t.md b/docs/mkdocs/docs/api/basic_json/json_base_class_t.md index 7add54098..f27e4e00b 100644 --- a/docs/mkdocs/docs/api/basic_json/json_base_class_t.md +++ b/docs/mkdocs/docs/api/basic_json/json_base_class_t.md @@ -27,6 +27,18 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s [standard layout](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType) guarantee. See [Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass). +#### Name conflicts + +Since `basic_json` derives from `CustomBaseClass`, members of `basic_json` hide members of `CustomBaseClass` with the +same name. Hidden members remain accessible via [`as_base_class`](as_base_class.md) or by casting the value to +`json_base_class_t`. + +!!! warning "Avoid generic member names" + + Future versions of the library may add members to `basic_json` that hide members of `CustomBaseClass` that are + accessible today. To reduce the risk of such conflicts, avoid generic names for the members of `CustomBaseClass`, + for instance by using a distinctive prefix. + ## Examples ??? example @@ -43,6 +55,11 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s --8<-- "examples/json_base_class_t.output" ``` +## See also + +- [as_base_class](as_base_class.md) - access the custom base class + ## Version history - Added in version 3.12.0. +- Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class. diff --git a/docs/mkdocs/docs/examples/as_base_class.cpp b/docs/mkdocs/docs/examples/as_base_class.cpp new file mode 100644 index 000000000..48357b045 --- /dev/null +++ b/docs/mkdocs/docs/examples/as_base_class.cpp @@ -0,0 +1,41 @@ +#include +#include + +class base_class_with_hidden_members +{ + public: + const char* type_name() const noexcept + { + return "my_type_name"; + } + + std::size_t size() const noexcept + { + return 42; + } +}; + +using json = nlohmann::basic_json < + std::map, + std::vector, + std::string, + bool, + std::int64_t, + std::uint64_t, + double, + std::allocator, + nlohmann::adl_serializer, + std::vector, + base_class_with_hidden_members + >; + +int main() +{ + json j = {1, 2, 3}; + + // the members of basic_json hide the members of the base class + std::cout << j.type_name() << ' ' << j.size() << '\n'; + + // access the hidden members of the base class + std::cout << j.as_base_class().type_name() << ' ' << j.as_base_class().size() << '\n'; +} diff --git a/docs/mkdocs/docs/examples/as_base_class.output b/docs/mkdocs/docs/examples/as_base_class.output new file mode 100644 index 000000000..5ca62a673 --- /dev/null +++ b/docs/mkdocs/docs/examples/as_base_class.output @@ -0,0 +1,2 @@ +array 3 +my_type_name 42 diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index a05ce2dff..3d2e95983 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -113,6 +113,7 @@ nav: - 'accept': api/basic_json/accept.md - 'array': api/basic_json/array.md - 'array_t': api/basic_json/array_t.md + - 'as_base_class': api/basic_json/as_base_class.md - 'at': api/basic_json/at.md - 'back': api/basic_json/back.md - 'begin': api/basic_json/begin.md diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index abd461d6c..efc0b11f6 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -153,7 +153,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; - using json_base_class_t = ::nlohmann::detail::json_base_class; JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; @@ -213,6 +212,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec using cbor_tag_handler_t = detail::cbor_tag_handler_t; /// how to encode BJData using bjdata_version_t = detail::bjdata_version_t; + /// base class used to inject custom functionality into each instance of basic_json + /// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/ + using json_base_class_t = ::nlohmann::detail::json_base_class; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list>; @@ -2690,6 +2692,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return *get_ptr(); } + /// @brief access the custom base class + /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/ + json_base_class_t& as_base_class() noexcept + { + return static_cast(*this); + } + + /// @brief access the custom base class + /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/ + const json_base_class_t& as_base_class() const noexcept + { + return static_cast(*this); + } + /// @} //////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 81ddfb1c4..83e81df7d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -25034,7 +25034,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; - using json_base_class_t = ::nlohmann::detail::json_base_class; JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; @@ -25094,6 +25093,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec using cbor_tag_handler_t = detail::cbor_tag_handler_t; /// how to encode BJData using bjdata_version_t = detail::bjdata_version_t; + /// base class used to inject custom functionality into each instance of basic_json + /// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/ + using json_base_class_t = ::nlohmann::detail::json_base_class; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list>; @@ -27571,6 +27573,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return *get_ptr(); } + /// @brief access the custom base class + /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/ + json_base_class_t& as_base_class() noexcept + { + return static_cast(*this); + } + + /// @brief access the custom base class + /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/ + const json_base_class_t& as_base_class() const noexcept + { + return static_cast(*this); + } + /// @} //////////////////// diff --git a/tests/src/unit-custom-base-class.cpp b/tests/src/unit-custom-base-class.cpp index 7dab5c576..c4b1dede8 100644 --- a/tests/src/unit-custom-base-class.cpp +++ b/tests/src/unit-custom-base-class.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "doctest_compatibility.h" @@ -333,3 +335,71 @@ TEST_CASE("JSON Visit Node") ); CHECK(expected.empty()); } + +// Test accessing members of a custom base class that are hidden by members of nlohmann::basic_json +class base_class_with_hidden_members +{ + public: + const char* type_name() const noexcept + { + return "custom type_name"; + } + + std::size_t size() const noexcept + { + return m_size; + } + + std::size_t m_size = 42; +}; + +using json_with_hidden_base_members = + nlohmann::basic_json < + std::map, + std::vector, + std::string, + bool, + std::int64_t, + std::uint64_t, + double, + std::allocator, + nlohmann::adl_serializer, + std::vector, + base_class_with_hidden_members + >; + +TEST_CASE("JSON Node as_base_class") +{ + using json = json_with_hidden_base_members; + + static_assert(std::is_same().as_base_class()), json::json_base_class_t&>::value, ""); + static_assert(std::is_same().as_base_class()), const json::json_base_class_t&>::value, ""); + static_assert(noexcept(std::declval().as_base_class()), ""); + static_assert(noexcept(std::declval().as_base_class()), ""); + + SECTION("non-const") + { + json j = {1, 2, 3}; + + CHECK(std::string(j.type_name()) == "array"); + CHECK(j.size() == 3); + CHECK(std::string(j.as_base_class().type_name()) == "custom type_name"); + CHECK(j.as_base_class().size() == 42); + CHECK(&j.as_base_class() == &static_cast(j)); + + j.as_base_class().m_size = 7; + CHECK(j.as_base_class().size() == 7); + CHECK(j.size() == 3); + } + + SECTION("const") + { + const json j = {1, 2, 3}; + + CHECK(std::string(j.type_name()) == "array"); + CHECK(j.size() == 3); + CHECK(std::string(j.as_base_class().type_name()) == "custom type_name"); + CHECK(j.as_base_class().size() == 42); + CHECK(&j.as_base_class() == &static_cast(j)); + } +}