Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom base class as node customization point #3110

Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
143f868
Allow to add a custom base class as an extension point to json nodes
barcode Oct 30, 2021
a131ecc
Fix code for msvc
barcode Oct 30, 2021
51d8c28
Fix test
barcode Oct 30, 2021
8f4d8a6
Re run make amalgamate
barcode Oct 31, 2021
09f42fd
Suppress fals positive of cppcheck
barcode Oct 31, 2021
e5534f1
Fix clang-tidy errors
barcode Oct 31, 2021
0046348
Try fixing code for clang-tidy
barcode Oct 31, 2021
ef54887
Remove make_unique to make the test c++11 compliant
barcode Nov 1, 2021
e41e315
Add NOLINT since clang-tidy requests make_unique, but C++11 jobs don'…
barcode Nov 1, 2021
f43c6b4
Document what the include of type_traits is used for
barcode Nov 1, 2021
5204ebb
Add documentation
barcode Nov 1, 2021
2e7e67d
Fix typo
barcode Nov 1, 2021
500fc0a
Remove accidentally duplicated doc text
barcode Nov 1, 2021
79a7056
Update / add documentation for custom base class
Jun 28, 2022
c427a52
Fix clang-tidy-15 warning about use after move
Jun 28, 2022
050a831
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Jul 5, 2022
21d0d38
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Jul 5, 2022
5539e6f
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Jul 5, 2022
0a8e16d
Adapt code to review
Jul 5, 2022
962fca2
readd suppress for cppcheck
Jul 12, 2022
7cd3033
run amalgamate
Aug 8, 2022
afb108a
Update include/nlohmann/detail/json_custom_base_class.hpp
barcode Aug 19, 2022
f516209
Update single_include/nlohmann/json.hpp
barcode Aug 19, 2022
0bcc12c
Update include/nlohmann/detail/json_custom_base_class.hpp
barcode Aug 19, 2022
b1001c6
Update single_include/nlohmann/json.hpp
barcode Aug 19, 2022
763c59d
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Aug 27, 2022
0ce9ded
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Aug 27, 2022
49ab721
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Aug 27, 2022
b0f5baf
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Aug 27, 2022
e9f5e0c
Update docs/mkdocs/docs/api/basic_json/json_base_class_t.md
barcode Aug 27, 2022
b3816f1
Update include/nlohmann/detail/json_custom_base_class.hpp
barcode Aug 27, 2022
7d476a0
make amalgamate
Aug 27, 2022
c5ede1a
Update include/nlohmann/detail/json_custom_base_class.hpp
barcode Aug 27, 2022
19423a7
make amalgamate
Aug 27, 2022
46597f0
Add include for nlohmann/detail/abi_macros.hpp
Aug 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions docs/examples/json_base_class_t.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
nlohmann marked this conversation as resolved.
Show resolved Hide resolved
}

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 changes: 4 additions & 0 deletions docs/examples/json_base_class_t.output
Original file line number Diff line number Diff line change
@@ -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
4 changes: 3 additions & 1 deletion docs/mkdocs/docs/api/basic_json/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ template<
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
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;
```
Expand All @@ -32,6 +33,7 @@ class basic_json;
| `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) |
| `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

Expand Down
44 changes: 44 additions & 0 deletions docs/mkdocs/docs/api/basic_json/json_base_class_t.md
Original file line number Diff line number Diff line change
@@ -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.
barcode marked this conversation as resolved.
Show resolved Hide resolved

## 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 is used and no additional functionality is injected.
barcode marked this conversation as resolved.
Show resolved Hide resolved

#### Limitations

The type `CustomBaseClass` has to be a default constructible class.
barcode marked this conversation as resolved.
Show resolved Hide resolved
`basic_json` only supports copy/move construction/assignment if `CustomBaseClass` does so as well.
barcode marked this conversation as resolved.
Show resolved Hide resolved

## 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 ?.?.?.
barcode marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions docs/mkdocs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ nav:
- 'is_string': api/basic_json/is_string.md
- 'is_structured': api/basic_json/is_structured.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
- 'max_size': api/basic_json/max_size.md
- 'meta': api/basic_json/meta.md
Expand Down
29 changes: 29 additions & 0 deletions include/nlohmann/detail/json_custom_base_class.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <type_traits> // conditional, is_same

NLOHMANN_JSON_NAMESPACE_BEGIN
barcode marked this conversation as resolved.
Show resolved Hide resolved
namespace detail
{

/*!
@brief Default base class of the @ref basic_json class.

So that the correct implementation of the copy / move ctors / assign operators
barcode marked this conversation as resolved.
Show resolved Hide resolved
of @ref basic_json does not require complex case distinctions
barcode marked this conversation as resolved.
Show resolved Hide resolved
(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>
nlohmann marked this conversation as resolved.
Show resolved Hide resolved
using json_base_class = typename std::conditional <
std::is_same<T, void>::value,
json_default_base,
T
>::type;

} // namespace detail
NLOHMANN_JSON_NAMESPACE_END
5 changes: 3 additions & 2 deletions include/nlohmann/detail/macro_scope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,13 @@
class NumberUnsignedType, class NumberFloatType, \
template<typename> class AllocatorType, \
template<typename, typename = void> class JSONSerializer, \
class BinaryType>
class BinaryType, \
class CustomBaseClass>

#define NLOHMANN_BASIC_JSON_TPL \
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
AllocatorType, JSONSerializer, BinaryType>
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>

// Macros to simplify conversion from/to types

Expand Down
15 changes: 11 additions & 4 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
#include <nlohmann/detail/iterators/json_reverse_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_ref.hpp>
#include <nlohmann/detail/macro_scope.hpp>
Expand Down Expand Up @@ -93,6 +94,7 @@ The invariants are checked by member function assert_invariant().
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
: public ::nlohmann::detail::json_base_class<CustomBaseClass>
{
private:
template<detail::value_t> friend struct detail::external_constructor;
Expand All @@ -119,6 +121,7 @@ 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<CustomBaseClass>;

JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
Expand Down Expand Up @@ -1132,7 +1135,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief copy constructor
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
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
other.assert_invariant();
Expand Down Expand Up @@ -1200,11 +1204,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief move constructor
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(basic_json&& other) noexcept
: m_type(std::move(other.m_type)),
: json_base_class_t(std::move(other)),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@barcode Sorry for being so late, but now I got a warning from Clang-Tidy about accessing other.m_value after moving other in the line before. Any idea how to fix this?

m_type(std::move(other.m_type)),
m_value(std::move(other.m_value))
{
// 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
other.m_type = value_t::null;
Expand All @@ -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_assignable<value_t>::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
Expand All @@ -1229,6 +1235,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using std::swap;
swap(m_type, other.m_type);
swap(m_value, other.m_value);
json_base_class_t::operator=(std::move(other));

set_parents();
assert_invariant();
Expand Down
3 changes: 2 additions & 1 deletion include/nlohmann/json_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer,
class BinaryType = std::vector<std::uint8_t>>
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
nlohmann marked this conversation as resolved.
Show resolved Hide resolved
class CustomBaseClass = void>
class basic_json;

/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
Expand Down
Loading