Add basic_json::as_base_class and document name conflicts with custom base classes (#5589)
* Add basic_json::as_base_class and document name conflicts with custom base classes Members of basic_json hide members of a custom base class with the same name, and future releases may add members that hide ones accessible today. Document this in json_base_class_t and add as_base_class() to reach hidden members without spelling out the cast. Also make json_base_class_t a public member type. It was documented since 3.12.0, but declared private, so users could not name it. Supersedes #3899. Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Add as_base_class to the docset search index New public members get an entry in docs/docset/docSet.sql (as done for to_bon8/from_bon8 in #2998). Without it, the Dash/Zeal docset built from the documentation cannot find basic_json::as_base_class. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence clang-tidy for the hidden type_name() in the base class test ci_clang_tidy failed with readability-convert-member-functions-to-static on base_class_with_hidden_members::type_name(). It must stay a non-static member: the test shows that it is hidden by the non-static basic_json::type_name() and reachable through as_base_class(). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
This commit is contained in:
1 parent
1a77948c25
commit
40021f38fb
10 files changed
+217
-2
No files matched your search
@@ -19,6 +19,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# <small>nlohmann::basic_json::</small>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<json_base_class_t&>(j)` (or `static_cast<const json_base_class_t&>(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.
|
||||||
@@ -200,6 +200,7 @@ Direct access to the stored value of a JSON value.
|
|||||||
- [**get_ref**](get_ref.md) - get a reference value
|
- [**get_ref**](get_ref.md) - get a reference value
|
||||||
- [**operator ValueType**](operator_ValueType.md) - get a value
|
- [**operator ValueType**](operator_ValueType.md) - get a value
|
||||||
- [**get_binary**](get_binary.md) - get a binary value
|
- [**get_binary**](get_binary.md) - get a binary value
|
||||||
|
- [**as_base_class**](as_base_class.md) - access the custom base class
|
||||||
|
|
||||||
### Element access
|
### Element access
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
[standard layout](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType) guarantee. See
|
||||||
[Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass).
|
[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
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -45,8 +57,10 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
|
|||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
|
- [as_base_class](as_base_class.md) - access the custom base class
|
||||||
- [Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass) - the requirements for `CustomBaseClass`
|
- [Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass) - the requirements for `CustomBaseClass`
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.12.0.
|
- 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.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
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<std::uint8_t>,
|
||||||
|
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';
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
array 3
|
||||||
|
my_type_name 42
|
||||||
@@ -116,6 +116,7 @@ nav:
|
|||||||
- 'accept': api/basic_json/accept.md
|
- 'accept': api/basic_json/accept.md
|
||||||
- 'array': api/basic_json/array.md
|
- 'array': api/basic_json/array.md
|
||||||
- 'array_t': api/basic_json/array_t.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
|
- 'at': api/basic_json/at.md
|
||||||
- 'back': api/basic_json/back.md
|
- 'back': api/basic_json/back.md
|
||||||
- 'begin': api/basic_json/begin.md
|
- 'begin': api/basic_json/begin.md
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
/// workaround type for MSVC
|
/// workaround type for MSVC
|
||||||
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
|
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
|
||||||
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
// convenience aliases for types residing in namespace detail;
|
// convenience aliases for types residing in namespace detail;
|
||||||
@@ -216,6 +215,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
|
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
|
||||||
/// how to encode BJData
|
/// how to encode BJData
|
||||||
using bjdata_version_t = detail::bjdata_version_t;
|
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<CustomBaseClass>;
|
||||||
/// helper type for initializer lists of basic_json values
|
/// helper type for initializer lists of basic_json values
|
||||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||||
|
|
||||||
@@ -3023,6 +3025,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return *get_ptr<const binary_t*>();
|
return *get_ptr<const binary_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @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<json_base_class_t&>(*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<const json_base_class_t&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -26699,7 +26699,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
/// workaround type for MSVC
|
/// workaround type for MSVC
|
||||||
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
|
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
|
||||||
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
// convenience aliases for types residing in namespace detail;
|
// convenience aliases for types residing in namespace detail;
|
||||||
@@ -26753,6 +26752,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
|
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
|
||||||
/// how to encode BJData
|
/// how to encode BJData
|
||||||
using bjdata_version_t = detail::bjdata_version_t;
|
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<CustomBaseClass>;
|
||||||
/// helper type for initializer lists of basic_json values
|
/// helper type for initializer lists of basic_json values
|
||||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||||
|
|
||||||
@@ -29560,6 +29562,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return *get_ptr<const binary_t*>();
|
return *get_ptr<const binary_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @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<json_base_class_t&>(*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<const json_base_class_t&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
@@ -406,6 +408,74 @@ TEST_CASE("JSON Visit Node")
|
|||||||
CHECK(expected.empty());
|
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 // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
{
|
||||||
|
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<std::uint8_t>,
|
||||||
|
base_class_with_hidden_members
|
||||||
|
>;
|
||||||
|
|
||||||
|
TEST_CASE("JSON Node as_base_class")
|
||||||
|
{
|
||||||
|
using json = json_with_hidden_base_members;
|
||||||
|
|
||||||
|
static_assert(std::is_same<decltype(std::declval<json&>().as_base_class()), json::json_base_class_t&>::value, "");
|
||||||
|
static_assert(std::is_same<decltype(std::declval<const json&>().as_base_class()), const json::json_base_class_t&>::value, "");
|
||||||
|
static_assert(noexcept(std::declval<json&>().as_base_class()), "");
|
||||||
|
static_assert(noexcept(std::declval<const json&>().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<json::json_base_class_t&>(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<const json::json_base_class_t&>(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A custom base class with a const member: copy-constructible (initializing a
|
// A custom base class with a const member: copy-constructible (initializing a
|
||||||
// const member works fine), but not copy-/move-assignable (assigning one does
|
// const member works fine), but not copy-/move-assignable (assigning one does
|
||||||
// not). Used to check that copy construction never requires more than that.
|
// not). Used to check that copy construction never requires more than that.
|
||||||
|
|||||||
Reference in new issue
Block a user