Allow custom base class as node customization point (#3110)
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com> Co-authored-by: Florian Albrechtskirchinger <falbrechtskirchinger@gmail.com> Co-authored-by: barcode <barcode@example.com>
This commit is contained in:
parent
f7973f46d6
commit
bed648ca55
88
docs/examples/json_base_class_t.cpp
Normal file
88
docs/examples/json_base_class_t.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
class visitor_adaptor_with_metadata
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <class Fnc>
|
||||||
|
void visit(const Fnc& fnc) const;
|
||||||
|
|
||||||
|
int metadata = 42;
|
||||||
|
private:
|
||||||
|
template <class Ptr, class Fnc>
|
||||||
|
void do_visit(const Ptr& ptr, const Fnc& fnc) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
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>,
|
||||||
|
visitor_adaptor_with_metadata
|
||||||
|
>;
|
||||||
|
|
||||||
|
template <class Fnc>
|
||||||
|
void visitor_adaptor_with_metadata::visit(const Fnc& fnc) const
|
||||||
|
{
|
||||||
|
do_visit(json::json_pointer{}, fnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Ptr, class Fnc>
|
||||||
|
void visitor_adaptor_with_metadata::do_visit(const Ptr& ptr, const Fnc& fnc) const
|
||||||
|
{
|
||||||
|
using value_t = nlohmann::detail::value_t;
|
||||||
|
const json& j = *static_cast<const json*>(this);
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
fnc(ptr, j);
|
||||||
|
for (const auto& entry : j.items())
|
||||||
|
{
|
||||||
|
entry.value().do_visit(ptr / entry.key(), fnc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value_t::array:
|
||||||
|
fnc(ptr, j);
|
||||||
|
for (std::size_t i = 0; i < j.size(); ++i)
|
||||||
|
{
|
||||||
|
j.at(i).do_visit(ptr / std::to_string(i), fnc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
fnc(ptr, j);
|
||||||
|
break;
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// create a json object
|
||||||
|
json j;
|
||||||
|
j["null"];
|
||||||
|
j["object"]["uint"] = 1U;
|
||||||
|
j["object"].metadata = 21;
|
||||||
|
|
||||||
|
// visit and output
|
||||||
|
j.visit(
|
||||||
|
[&](const json::json_pointer & p,
|
||||||
|
const json & j)
|
||||||
|
{
|
||||||
|
std::cout << (p.empty() ? std::string{"/"} : p.to_string())
|
||||||
|
<< " - metadata = " << j.metadata << " -> " << j.dump() << '\n';
|
||||||
|
});
|
||||||
|
}
|
4
docs/examples/json_base_class_t.output
Normal file
4
docs/examples/json_base_class_t.output
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/ - metadata = 42 -> {"null":null,"object":{"uint":1}}
|
||||||
|
/null - metadata = 42 -> null
|
||||||
|
/object - metadata = 21 -> {"uint":1}
|
||||||
|
/object/uint - metadata = 42 -> 1
|
@ -13,7 +13,8 @@ template<
|
|||||||
class NumberFloatType = double,
|
class NumberFloatType = double,
|
||||||
template<typename U> class AllocatorType = std::allocator,
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer,
|
template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>
|
class BinaryType = std::vector<std::uint8_t,
|
||||||
|
class CustomBaseClass = void>
|
||||||
>
|
>
|
||||||
class basic_json;
|
class basic_json;
|
||||||
```
|
```
|
||||||
@ -32,6 +33,7 @@ class basic_json;
|
|||||||
| `AllocatorType` | type of the allocator to use | |
|
| `AllocatorType` | type of the allocator to use | |
|
||||||
| `JSONSerializer` | the serializer to resolve internal calls to `to_json()` and `from_json()` | [`json_serializer`](json_serializer.md) |
|
| `JSONSerializer` | the serializer to resolve internal calls to `to_json()` and `from_json()` | [`json_serializer`](json_serializer.md) |
|
||||||
| `BinaryType` | type for binary arrays | [`binary_t`](binary_t.md) |
|
| `BinaryType` | type for binary arrays | [`binary_t`](binary_t.md) |
|
||||||
|
| `CustomBaseClass` | extension point for user code | [`json_base_class_t`](json_base_class_t.md) |
|
||||||
|
|
||||||
## Specializations
|
## Specializations
|
||||||
|
|
||||||
|
44
docs/mkdocs/docs/api/basic_json/json_base_class_t.md
Normal file
44
docs/mkdocs/docs/api/basic_json/json_base_class_t.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# <small>nlohmann::basic_json::</small>json_base_class_t
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using json_base_class_t = detail::json_base_class<CustomBaseClass>;
|
||||||
|
```
|
||||||
|
|
||||||
|
The base class used to inject custom functionality into each instance of `basic_json`.
|
||||||
|
Examples of such functionality might be metadata, additional member functions (e.g., visitors), or other application-specific code.
|
||||||
|
|
||||||
|
## Template parameters
|
||||||
|
|
||||||
|
`CustomBaseClass`
|
||||||
|
: the base class to be added to `basic_json`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
#### Default type
|
||||||
|
|
||||||
|
The default value for `CustomBaseClass` is `void`. In this case an [empty base class](https://en.cppreference.com/w/cpp/language/ebo) is used and no additional functionality is injected.
|
||||||
|
|
||||||
|
#### Limitations
|
||||||
|
|
||||||
|
The type `CustomBaseClass` has to be a default-constructible class.
|
||||||
|
`basic_json` only supports copy/move construction/assignment if `CustomBaseClass` does so as well.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The following code shows how to inject custom data and methods for each node.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/json_base_class_t.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/json_base_class_t.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.12.0.
|
@ -141,6 +141,7 @@ nav:
|
|||||||
- 'is_string': api/basic_json/is_string.md
|
- 'is_string': api/basic_json/is_string.md
|
||||||
- 'is_structured': api/basic_json/is_structured.md
|
- 'is_structured': api/basic_json/is_structured.md
|
||||||
- 'items': api/basic_json/items.md
|
- 'items': api/basic_json/items.md
|
||||||
|
- 'json_base_class_t': api/basic_json/json_base_class_t.md
|
||||||
- 'json_serializer': api/basic_json/json_serializer.md
|
- 'json_serializer': api/basic_json/json_serializer.md
|
||||||
- 'max_size': api/basic_json/max_size.md
|
- 'max_size': api/basic_json/max_size.md
|
||||||
- 'meta': api/basic_json/meta.md
|
- 'meta': api/basic_json/meta.md
|
||||||
|
31
include/nlohmann/detail/json_custom_base_class.hpp
Normal file
31
include/nlohmann/detail/json_custom_base_class.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits> // conditional, is_same
|
||||||
|
|
||||||
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Default base class of the @ref basic_json class.
|
||||||
|
|
||||||
|
So that the correct implementations of the copy / move ctors / assign operators
|
||||||
|
of @ref basic_json do not require complex case distinctions
|
||||||
|
(no base class / custom base class used as customization point),
|
||||||
|
@ref basic_json always has a base class.
|
||||||
|
By default, this class is used because it is empty and thus has no effect
|
||||||
|
on the behavior of @ref basic_json.
|
||||||
|
*/
|
||||||
|
struct json_default_base {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using json_base_class = typename std::conditional <
|
||||||
|
std::is_same<T, void>::value,
|
||||||
|
json_default_base,
|
||||||
|
T
|
||||||
|
>::type;
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
@ -240,12 +240,13 @@
|
|||||||
class NumberUnsignedType, class NumberFloatType, \
|
class NumberUnsignedType, class NumberFloatType, \
|
||||||
template<typename> class AllocatorType, \
|
template<typename> class AllocatorType, \
|
||||||
template<typename, typename = void> class JSONSerializer, \
|
template<typename, typename = void> class JSONSerializer, \
|
||||||
class BinaryType>
|
class BinaryType, \
|
||||||
|
class CustomBaseClass>
|
||||||
|
|
||||||
#define NLOHMANN_BASIC_JSON_TPL \
|
#define NLOHMANN_BASIC_JSON_TPL \
|
||||||
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
||||||
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
||||||
AllocatorType, JSONSerializer, BinaryType>
|
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>
|
||||||
|
|
||||||
// Macros to simplify conversion from/to types
|
// Macros to simplify conversion from/to types
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
||||||
#include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
|
#include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
|
||||||
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
||||||
|
#include <nlohmann/detail/json_custom_base_class.hpp>
|
||||||
#include <nlohmann/detail/json_pointer.hpp>
|
#include <nlohmann/detail/json_pointer.hpp>
|
||||||
#include <nlohmann/detail/json_ref.hpp>
|
#include <nlohmann/detail/json_ref.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
@ -93,6 +94,7 @@ The invariants are checked by member function assert_invariant().
|
|||||||
*/
|
*/
|
||||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
||||||
|
: public ::nlohmann::detail::json_base_class<CustomBaseClass>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
template<detail::value_t> friend struct detail::external_constructor;
|
template<detail::value_t> friend struct detail::external_constructor;
|
||||||
@ -119,6 +121,7 @@ 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;
|
||||||
@ -1132,7 +1135,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @brief copy constructor
|
/// @brief copy constructor
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||||
basic_json(const basic_json& other)
|
basic_json(const basic_json& other)
|
||||||
: m_type(other.m_type)
|
: json_base_class_t(other),
|
||||||
|
m_type(other.m_type)
|
||||||
{
|
{
|
||||||
// check of passed value is valid
|
// check of passed value is valid
|
||||||
other.assert_invariant();
|
other.assert_invariant();
|
||||||
@ -1200,11 +1204,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @brief move constructor
|
/// @brief move constructor
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||||
basic_json(basic_json&& other) noexcept
|
basic_json(basic_json&& other) noexcept
|
||||||
: m_type(std::move(other.m_type)),
|
: json_base_class_t(std::move(other)),
|
||||||
|
m_type(std::move(other.m_type)),
|
||||||
m_value(std::move(other.m_value))
|
m_value(std::move(other.m_value))
|
||||||
{
|
{
|
||||||
// check that passed value is valid
|
// check that passed value is valid
|
||||||
other.assert_invariant(false);
|
other.assert_invariant(false); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||||
|
|
||||||
// invalidate payload
|
// invalidate payload
|
||||||
other.m_type = value_t::null;
|
other.m_type = value_t::null;
|
||||||
@ -1220,7 +1225,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
std::is_nothrow_move_constructible<value_t>::value&&
|
std::is_nothrow_move_constructible<value_t>::value&&
|
||||||
std::is_nothrow_move_assignable<value_t>::value&&
|
std::is_nothrow_move_assignable<value_t>::value&&
|
||||||
std::is_nothrow_move_constructible<json_value>::value&&
|
std::is_nothrow_move_constructible<json_value>::value&&
|
||||||
std::is_nothrow_move_assignable<json_value>::value
|
std::is_nothrow_move_assignable<json_value>::value&&
|
||||||
|
std::is_nothrow_move_assignable<json_base_class_t>::value
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// check that passed value is valid
|
// check that passed value is valid
|
||||||
@ -1229,6 +1235,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using std::swap;
|
using std::swap;
|
||||||
swap(m_type, other.m_type);
|
swap(m_type, other.m_type);
|
||||||
swap(m_value, other.m_value);
|
swap(m_value, other.m_value);
|
||||||
|
json_base_class_t::operator=(std::move(other));
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
|
@ -46,7 +46,8 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
|
|||||||
template<typename U> class AllocatorType = std::allocator,
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>>
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
|
class CustomBaseClass = void>
|
||||||
class basic_json;
|
class basic_json;
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
|
@ -2590,12 +2590,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP
|
|||||||
class NumberUnsignedType, class NumberFloatType, \
|
class NumberUnsignedType, class NumberFloatType, \
|
||||||
template<typename> class AllocatorType, \
|
template<typename> class AllocatorType, \
|
||||||
template<typename, typename = void> class JSONSerializer, \
|
template<typename, typename = void> class JSONSerializer, \
|
||||||
class BinaryType>
|
class BinaryType, \
|
||||||
|
class CustomBaseClass>
|
||||||
|
|
||||||
#define NLOHMANN_BASIC_JSON_TPL \
|
#define NLOHMANN_BASIC_JSON_TPL \
|
||||||
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
||||||
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
||||||
AllocatorType, JSONSerializer, BinaryType>
|
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>
|
||||||
|
|
||||||
// Macros to simplify conversion from/to types
|
// Macros to simplify conversion from/to types
|
||||||
|
|
||||||
@ -3389,7 +3390,8 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
template<typename U> class AllocatorType = std::allocator,
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>>
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
|
class CustomBaseClass = void>
|
||||||
class basic_json;
|
class basic_json;
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
@ -13673,6 +13675,40 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/json_custom_base_class.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#include <type_traits> // conditional, is_same
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Default base class of the @ref basic_json class.
|
||||||
|
|
||||||
|
So that the correct implementations of the copy / move ctors / assign operators
|
||||||
|
of @ref basic_json do not require complex case distinctions
|
||||||
|
(no base class / custom base class used as customization point),
|
||||||
|
@ref basic_json always has a base class.
|
||||||
|
By default, this class is used because it is empty and thus has no effect
|
||||||
|
on the behavior of @ref basic_json.
|
||||||
|
*/
|
||||||
|
struct json_default_base {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using json_base_class = typename std::conditional <
|
||||||
|
std::is_same<T, void>::value,
|
||||||
|
json_default_base,
|
||||||
|
T
|
||||||
|
>::type;
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
// #include <nlohmann/detail/json_pointer.hpp>
|
// #include <nlohmann/detail/json_pointer.hpp>
|
||||||
// __ _____ _____ _____
|
// __ _____ _____ _____
|
||||||
// __| | __| | | | JSON for Modern C++
|
// __| | __| | | | JSON for Modern C++
|
||||||
@ -19271,6 +19307,7 @@ The invariants are checked by member function assert_invariant().
|
|||||||
*/
|
*/
|
||||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
||||||
|
: public ::nlohmann::detail::json_base_class<CustomBaseClass>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
template<detail::value_t> friend struct detail::external_constructor;
|
template<detail::value_t> friend struct detail::external_constructor;
|
||||||
@ -19297,6 +19334,7 @@ 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;
|
||||||
@ -20310,7 +20348,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @brief copy constructor
|
/// @brief copy constructor
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||||
basic_json(const basic_json& other)
|
basic_json(const basic_json& other)
|
||||||
: m_type(other.m_type)
|
: json_base_class_t(other),
|
||||||
|
m_type(other.m_type)
|
||||||
{
|
{
|
||||||
// check of passed value is valid
|
// check of passed value is valid
|
||||||
other.assert_invariant();
|
other.assert_invariant();
|
||||||
@ -20378,11 +20417,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @brief move constructor
|
/// @brief move constructor
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||||
basic_json(basic_json&& other) noexcept
|
basic_json(basic_json&& other) noexcept
|
||||||
: m_type(std::move(other.m_type)),
|
: json_base_class_t(std::move(other)),
|
||||||
|
m_type(std::move(other.m_type)),
|
||||||
m_value(std::move(other.m_value))
|
m_value(std::move(other.m_value))
|
||||||
{
|
{
|
||||||
// check that passed value is valid
|
// check that passed value is valid
|
||||||
other.assert_invariant(false);
|
other.assert_invariant(false); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||||
|
|
||||||
// invalidate payload
|
// invalidate payload
|
||||||
other.m_type = value_t::null;
|
other.m_type = value_t::null;
|
||||||
@ -20398,7 +20438,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
std::is_nothrow_move_constructible<value_t>::value&&
|
std::is_nothrow_move_constructible<value_t>::value&&
|
||||||
std::is_nothrow_move_assignable<value_t>::value&&
|
std::is_nothrow_move_assignable<value_t>::value&&
|
||||||
std::is_nothrow_move_constructible<json_value>::value&&
|
std::is_nothrow_move_constructible<json_value>::value&&
|
||||||
std::is_nothrow_move_assignable<json_value>::value
|
std::is_nothrow_move_assignable<json_value>::value&&
|
||||||
|
std::is_nothrow_move_assignable<json_base_class_t>::value
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// check that passed value is valid
|
// check that passed value is valid
|
||||||
@ -20407,6 +20448,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using std::swap;
|
using std::swap;
|
||||||
swap(m_type, other.m_type);
|
swap(m_type, other.m_type);
|
||||||
swap(m_value, other.m_value);
|
swap(m_value, other.m_value);
|
||||||
|
json_base_class_t::operator=(std::move(other));
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
|
@ -147,7 +147,8 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
|
|||||||
template<typename U> class AllocatorType = std::allocator,
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>>
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
|
class CustomBaseClass = void>
|
||||||
class basic_json;
|
class basic_json;
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
|
357
tests/src/unit-custom-base-class.cpp
Normal file
357
tests/src/unit-custom-base-class.cpp
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/*
|
||||||
|
__ _____ _____ _____
|
||||||
|
__| | __| | | | JSON for Modern C++ (test suite)
|
||||||
|
| | |__ | | | | | | version 3.10.2
|
||||||
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// Test extending nlohmann::json by using a custom base class.
|
||||||
|
// Add some metadata to each node and test the behaviour of copy / move
|
||||||
|
template<class MetaDataType>
|
||||||
|
class json_metadata
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using metadata_t = MetaDataType;
|
||||||
|
metadata_t& metadata()
|
||||||
|
{
|
||||||
|
return m_metadata;
|
||||||
|
}
|
||||||
|
const metadata_t& metadata() const
|
||||||
|
{
|
||||||
|
return m_metadata;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
metadata_t m_metadata = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using json_with_metadata =
|
||||||
|
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>,
|
||||||
|
json_metadata<T>
|
||||||
|
>;
|
||||||
|
|
||||||
|
TEST_CASE("JSON Node Metadata")
|
||||||
|
{
|
||||||
|
SECTION("type int")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<int>;
|
||||||
|
json null;
|
||||||
|
auto obj = json::object();
|
||||||
|
auto array = json::array();
|
||||||
|
|
||||||
|
null.metadata() = 1;
|
||||||
|
obj.metadata() = 2;
|
||||||
|
array.metadata() = 3;
|
||||||
|
auto copy = array;
|
||||||
|
|
||||||
|
CHECK(null.metadata() == 1);
|
||||||
|
CHECK(obj.metadata() == 2);
|
||||||
|
CHECK(array.metadata() == 3);
|
||||||
|
CHECK(copy.metadata() == 3);
|
||||||
|
}
|
||||||
|
SECTION("type vector<int>")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::vector<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().emplace_back(1);
|
||||||
|
auto copy = value;
|
||||||
|
value.metadata().emplace_back(2);
|
||||||
|
|
||||||
|
CHECK(copy.metadata().size() == 1);
|
||||||
|
CHECK(copy.metadata().at(0) == 1);
|
||||||
|
CHECK(value.metadata().size() == 2);
|
||||||
|
CHECK(value.metadata().at(0) == 1);
|
||||||
|
CHECK(value.metadata().at(1) == 2);
|
||||||
|
}
|
||||||
|
SECTION("copy ctor")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::vector<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().emplace_back(1);
|
||||||
|
value.metadata().emplace_back(2);
|
||||||
|
|
||||||
|
json copy = value;
|
||||||
|
|
||||||
|
CHECK(copy.metadata().size() == 2);
|
||||||
|
CHECK(copy.metadata().at(0) == 1);
|
||||||
|
CHECK(copy.metadata().at(1) == 2);
|
||||||
|
CHECK(value.metadata().size() == 2);
|
||||||
|
CHECK(value.metadata().at(0) == 1);
|
||||||
|
CHECK(value.metadata().at(1) == 2);
|
||||||
|
|
||||||
|
value.metadata().clear();
|
||||||
|
CHECK(copy.metadata().size() == 2);
|
||||||
|
CHECK(value.metadata().size() == 0);
|
||||||
|
}
|
||||||
|
SECTION("move ctor")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::vector<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().emplace_back(1);
|
||||||
|
value.metadata().emplace_back(2);
|
||||||
|
|
||||||
|
const json moved = std::move(value);
|
||||||
|
|
||||||
|
CHECK(moved.metadata().size() == 2);
|
||||||
|
CHECK(moved.metadata().at(0) == 1);
|
||||||
|
CHECK(moved.metadata().at(1) == 2);
|
||||||
|
}
|
||||||
|
SECTION("move assign")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::vector<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().emplace_back(1);
|
||||||
|
value.metadata().emplace_back(2);
|
||||||
|
|
||||||
|
json moved;
|
||||||
|
moved = std::move(value);
|
||||||
|
|
||||||
|
CHECK(moved.metadata().size() == 2);
|
||||||
|
CHECK(moved.metadata().at(0) == 1);
|
||||||
|
CHECK(moved.metadata().at(1) == 2);
|
||||||
|
}
|
||||||
|
SECTION("copy assign")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::vector<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().emplace_back(1);
|
||||||
|
value.metadata().emplace_back(2);
|
||||||
|
|
||||||
|
json copy;
|
||||||
|
copy = value;
|
||||||
|
|
||||||
|
CHECK(copy.metadata().size() == 2);
|
||||||
|
CHECK(copy.metadata().at(0) == 1);
|
||||||
|
CHECK(copy.metadata().at(1) == 2);
|
||||||
|
CHECK(value.metadata().size() == 2);
|
||||||
|
CHECK(value.metadata().at(0) == 1);
|
||||||
|
CHECK(value.metadata().at(1) == 2);
|
||||||
|
|
||||||
|
value.metadata().clear();
|
||||||
|
CHECK(copy.metadata().size() == 2);
|
||||||
|
CHECK(value.metadata().size() == 0);
|
||||||
|
}
|
||||||
|
SECTION("type unique_ptr<int>")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::unique_ptr<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().reset(new int(42)); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
|
auto moved = std::move(value);
|
||||||
|
|
||||||
|
CHECK(moved.metadata() != nullptr);
|
||||||
|
CHECK(*moved.metadata() == 42);
|
||||||
|
}
|
||||||
|
SECTION("type vector<int> in json array")
|
||||||
|
{
|
||||||
|
using json = json_with_metadata<std::vector<int>>;
|
||||||
|
json value;
|
||||||
|
value.metadata().emplace_back(1);
|
||||||
|
value.metadata().emplace_back(2);
|
||||||
|
|
||||||
|
json array(10, value);
|
||||||
|
|
||||||
|
CHECK(value.metadata().size() == 2);
|
||||||
|
CHECK(value.metadata().at(0) == 1);
|
||||||
|
CHECK(value.metadata().at(1) == 2);
|
||||||
|
|
||||||
|
for (const auto& val : array)
|
||||||
|
{
|
||||||
|
CHECK(val.metadata().size() == 2);
|
||||||
|
CHECK(val.metadata().at(0) == 1);
|
||||||
|
CHECK(val.metadata().at(1) == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test extending nlohmann::json by using a custom base class.
|
||||||
|
// Add a custom member function template iterating over the whole json tree.
|
||||||
|
class visitor_adaptor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <class Fnc>
|
||||||
|
void visit(const Fnc& fnc) const;
|
||||||
|
private:
|
||||||
|
template <class Ptr, class Fnc>
|
||||||
|
void do_visit(const Ptr& ptr, const Fnc& fnc) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
using json_with_visitor_t = 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>,
|
||||||
|
visitor_adaptor
|
||||||
|
>;
|
||||||
|
|
||||||
|
|
||||||
|
template <class Fnc>
|
||||||
|
void visitor_adaptor::visit(const Fnc& fnc) const
|
||||||
|
{
|
||||||
|
do_visit(json_with_visitor_t::json_pointer{}, fnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Ptr, class Fnc>
|
||||||
|
void visitor_adaptor::do_visit(const Ptr& ptr, const Fnc& fnc) const
|
||||||
|
{
|
||||||
|
using value_t = nlohmann::detail::value_t;
|
||||||
|
const json_with_visitor_t& json = *static_cast<const json_with_visitor_t*>(this);
|
||||||
|
switch (json.type())
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
for (const auto& entry : json.items())
|
||||||
|
{
|
||||||
|
entry.value().do_visit(ptr / entry.key(), fnc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value_t::array:
|
||||||
|
for (std::size_t i = 0; i < json.size(); ++i)
|
||||||
|
{
|
||||||
|
json.at(i).do_visit(ptr / std::to_string(i), fnc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value_t::discarded:
|
||||||
|
break;
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
default:
|
||||||
|
fnc(ptr, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("JSON Visit Node")
|
||||||
|
{
|
||||||
|
json_with_visitor_t json;
|
||||||
|
json["null"];
|
||||||
|
json["int"] = -1;
|
||||||
|
json["uint"] = 1U;
|
||||||
|
json["float"] = 1.0;
|
||||||
|
json["boolean"] = true;
|
||||||
|
json["string"] = "string";
|
||||||
|
json["array"].push_back(0);
|
||||||
|
json["array"].push_back(1);
|
||||||
|
json["array"].push_back(json);
|
||||||
|
|
||||||
|
std::set<std::string> expected
|
||||||
|
{
|
||||||
|
"/null - null - null",
|
||||||
|
"/int - number_integer - -1",
|
||||||
|
"/uint - number_unsigned - 1",
|
||||||
|
"/float - number_float - 1.0",
|
||||||
|
"/boolean - boolean - true",
|
||||||
|
"/string - string - \"string\"",
|
||||||
|
"/array/0 - number_integer - 0",
|
||||||
|
"/array/1 - number_integer - 1",
|
||||||
|
|
||||||
|
"/array/2/null - null - null",
|
||||||
|
"/array/2/int - number_integer - -1",
|
||||||
|
"/array/2/uint - number_unsigned - 1",
|
||||||
|
"/array/2/float - number_float - 1.0",
|
||||||
|
"/array/2/boolean - boolean - true",
|
||||||
|
"/array/2/string - string - \"string\"",
|
||||||
|
"/array/2/array/0 - number_integer - 0",
|
||||||
|
"/array/2/array/1 - number_integer - 1"
|
||||||
|
};
|
||||||
|
|
||||||
|
json.visit(
|
||||||
|
[&](const json_with_visitor_t::json_pointer & p,
|
||||||
|
const json_with_visitor_t& j)
|
||||||
|
{
|
||||||
|
std::stringstream str;
|
||||||
|
str << p.to_string() << " - " ;
|
||||||
|
using value_t = nlohmann::detail::value_t;
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
str << "object";
|
||||||
|
break;
|
||||||
|
case value_t::array:
|
||||||
|
str << "array";
|
||||||
|
break;
|
||||||
|
case value_t::discarded:
|
||||||
|
str << "discarded";
|
||||||
|
break;
|
||||||
|
case value_t::null:
|
||||||
|
str << "null";
|
||||||
|
break;
|
||||||
|
case value_t::string:
|
||||||
|
str << "string";
|
||||||
|
break;
|
||||||
|
case value_t::boolean:
|
||||||
|
str << "boolean";
|
||||||
|
break;
|
||||||
|
case value_t::number_integer:
|
||||||
|
str << "number_integer";
|
||||||
|
break;
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
str << "number_unsigned";
|
||||||
|
break;
|
||||||
|
case value_t::number_float:
|
||||||
|
str << "number_float";
|
||||||
|
break;
|
||||||
|
case value_t::binary:
|
||||||
|
str << "binary";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str << "error";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str << " - " << j.dump();
|
||||||
|
CHECK(json.at(p) == j);
|
||||||
|
INFO(str.str());
|
||||||
|
CHECK(expected.count(str.str()) == 1);
|
||||||
|
expected.erase(str.str());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
CHECK(expected.empty());
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user