Add apply*() functions (visitor pattern)

Add 3 variants of apply*() functions that invoke a given callable with
the supplied arguments and the stored JSON value.

* void apply(Fn, Args...):
  ...

* void apply_cb(ResultCallback, Fn, Args...):
  ...

* R apply_r(Fn, Args...):
  ...
This commit is contained in:
Florian Albrechtskirchinger 2022-04-19 14:14:45 +02:00
parent 0c929e1721
commit f898c19aca
No known key found for this signature in database
GPG Key ID: 19618CE9B2D4BE6D
8 changed files with 1625 additions and 20 deletions

View File

@ -733,6 +733,28 @@ The dynamic type of the object cannot be represented in the requested serializat
Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}`
### json.exception.type_error.318
The given callable cannot be invoked with the stored JSON value and the arguments provided.
!!! failure "Example message"
Calling `apply()` on a numeric JSON value with an unary non-member function accepting a string:
```
[json.exception.type_error.318] cannot invoke callable with JSON value of type number
```
### json.exception.type_error.319
The result callback cannot be invoked with the callback argument, in case of a non-static member function pointer callback, and the result of the invocation of the callable.
!!! failure "Example message"
Calling `apply_cb()` with an unary non-member function result callback when the result of the invocation of the callable is not convertible to the argument type of the result callback:
```
[json.exception.type_error.319] cannot invoke callback
```
## Out of range
This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys.

View File

@ -136,6 +136,46 @@ using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
#endif
#ifdef JSON_HAS_CPP_17
// the following utilities are natively available in C++17
using std::conjunction;
using std::disjunction;
using std::negation;
using std::as_const;
#else
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type {};
template<class B> struct conjunction<B> : B {};
template<class B, class... Bn>
struct conjunction<B, Bn...>
: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
// https://en.cppreference.com/w/cpp/types/disjunction
template<class...> struct disjunction : std::false_type {};
template<class B> struct disjunction<B> : B {};
template<class B, class... Bn>
struct disjunction<B, Bn...>
: std::conditional<bool(B::value), B, disjunction<Bn...>>::type {};
// https://en.cppreference.com/w/cpp/types/negation
template<class B> struct negation : std::integral_constant < bool, !B::value > {};
// https://en.cppreference.com/w/cpp/utility/as_const
template <typename T>
constexpr typename std::add_const<T>::type& as_const(T& t) noexcept
{
return t;
}
template <typename T>
void as_const(const T&&) = delete;
#endif
// dispatch utility (taken from ranges-v3)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
template<> struct priority_tag<0> {};

View File

@ -0,0 +1,120 @@
#pragma once
#ifdef JSON_HAS_CPP_17
#include <functional> // invoke
#endif
#include <type_traits> // invoke_result, is_base_of, is_function, is_invocable, is_object, is_same, remove_reference
#include <utility> // declval, forward
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
#ifdef JSON_HAS_CPP_17
// the following utilities are natively available in C++17
using std::invoke;
using std::invoke_result;
using std::is_invocable;
#else
// invoke_impl derived from the C++ standard draft [func.require] for qslib (proprietary)
// modified to use standard library type traits and utilities where possible
namespace internal
{
template<typename Fn, typename... Args,
typename = decltype(std::declval<Fn>()(std::forward<Args>(std::declval<Args>())...))>
auto invoke_impl(Fn && f, Args && ...args) -> decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
};
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_function<T>::value
&& std::is_base_of<U, typename std::remove_reference<V>::type>::value, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ...args) -> decltype((v.*f)(std::forward<Args>(args)...))
{
return (v.*f)(std::forward<Args>(args)...);
}
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_function<T>::value
&& !std::is_base_of<U, typename std::remove_reference<V>::type>::value, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ...args) -> decltype(((*v).*f)(std::forward<Args>(args)...))
{
return ((*v).*f)(std::forward<Args>(args)...);
}
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_object<T>::value
&& sizeof...(Args) == 0, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ... /*unused*/) -> decltype((*v).*f)
{
return (*v).*f;
}
template <typename Fn, typename... Args>
using detect_invocable = decltype(invoke_impl(std::declval<Fn>(), std::declval<Args>()...));
// https://en.cppreference.com/w/cpp/types/result_of
template <typename AlwaysVoid, typename, typename...>
struct invoke_result {};
template <typename Fn, typename...Args>
struct invoke_result<decltype(void(invoke_impl(std::declval<Fn>(), std::declval<Args>()...))), Fn, Args...>
{
using type = decltype(invoke_impl(std::declval<Fn>(), std::declval<Args>()...));
};
} // namespace internal
template <typename Fn, typename... Args>
auto invoke(Fn&& f, Args&& ... args) -> decltype(internal::invoke_impl(std::forward<Fn>(f), std::forward<Args>(args)...))
{
return internal::invoke_impl(std::forward<Fn>(f), std::forward<Args>(args)...);
}
template <typename Fn, typename... Args>
using invoke_result = internal::invoke_result<void, Fn, Args...>;
template <typename Fn, typename... Args>
using is_invocable = typename is_detected<internal::detect_invocable, Fn, Args...>::type;
#endif
// used as a dummy argument
struct null_arg_t
{
explicit null_arg_t() = default;
};
static constexpr null_arg_t null_arg{};
template<typename T>
using is_null_arg = typename std::is_same<uncvref_t<T>, null_arg_t>::type;
template<typename Value, typename Tuple, std::size_t I>
struct apply_value_or_arg
{
using element_type = typename std::tuple_element<I, Tuple>::type;
using type = typename std::conditional<detail::is_basic_json_value_placeholder<element_type>::value, Value, element_type>::type;
};
template<typename Value, typename Fn, typename Tuple, std::size_t... I>
using apply_invoke_result_t = typename detail::invoke_result<Fn,
typename apply_value_or_arg<Value, Tuple, I>::type...>::type;
template<typename Value, typename Fn, typename Tuple, std::size_t... I>
using apply_is_invocable = typename detail::is_invocable<Fn,
typename apply_value_or_arg<Value, Tuple, I>::type...>::type;
} // namespace detail
} // namespace nlohmann

View File

@ -12,6 +12,7 @@
#include <nlohmann/detail/meta/call_std/end.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/placeholders.hpp>
#include <nlohmann/json_fwd.hpp>
namespace nlohmann
@ -178,16 +179,6 @@ using actual_object_comparator_t = typename actual_object_comparator<BasicJsonTy
// is_ functions //
///////////////////
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type { };
template<class B> struct conjunction<B> : B { };
template<class B, class... Bn>
struct conjunction<B, Bn...>
: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
// https://en.cppreference.com/w/cpp/types/negation
template<class B> struct negation : std::integral_constant < bool, !B::value > { };
// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
@ -564,6 +555,11 @@ struct is_ordered_map
enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
};
// checks if a type is a basic_json_value placeholder
template<typename T>
using is_basic_json_value_placeholder = typename std::is_same <
uncvref_t<T>, uncvref_t<decltype(::nlohmann::placeholders::basic_json_value) >>::type;
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
T conditional_static_cast(U value)
@ -577,5 +573,17 @@ T conditional_static_cast(U value)
return value;
}
// non-standard conditional version of as_const
template <bool AsConst, typename T>
constexpr typename std::conditional<AsConst,
typename std::add_const<T>::type, T>::type&
conditional_as_const(T& t) noexcept
{
return t;
}
template <bool AsConst, typename T>
void conditional_as_const(const T&&) = delete;
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,24 @@
#pragma once
namespace nlohmann
{
namespace detail
{
template<int I>
struct placeholder_t
{
static constexpr int value = I;
explicit placeholder_t() = default;
};
} // namespace detail
namespace placeholders
{
static constexpr detail::placeholder_t < -1 > basic_json_value{};
} // namespace placeholders
} // namespace nlohmann

View File

@ -62,6 +62,7 @@ SOFTWARE.
#include <memory> // unique_ptr
#include <numeric> // accumulate
#include <string> // string, stoi, to_string
#include <tuple> // forward_as_tuple, tuple
#include <utility> // declval, forward, move, pair, swap
#include <vector> // vector
@ -86,10 +87,12 @@ SOFTWARE.
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/invoke.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/output/binary_writer.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/output/serializer.hpp>
#include <nlohmann/detail/placeholders.hpp>
#include <nlohmann/detail/value_t.hpp>
#include <nlohmann/json_fwd.hpp>
#include <nlohmann/ordered_map.hpp>
@ -207,6 +210,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// SAX interface type, see @ref nlohmann::json_sax
using json_sax_t = json_sax<basic_json>;
// placeholder for the stored JSON value used in apply*() functions
static constexpr decltype(::nlohmann::placeholders::basic_json_value) value_placeholder
= ::nlohmann::placeholders::basic_json_value;
////////////////
// exceptions //
////////////////
@ -5120,8 +5127,267 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
/// @}
private:
template<typename Arg, typename Value,
detail::enable_if_t<detail::is_basic_json_value_placeholder<Arg>::value, int> = 0>
static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward<Value>(val))
{
return std::forward<Value>(val);
}
template < typename Arg, typename Value,
detail::enable_if_t < !detail::is_basic_json_value_placeholder<Arg>::value, int > = 0 >
static auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) -> decltype(std::forward<Arg>(arg))
{
return std::forward<Arg>(arg);
}
// invoke the result callback
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, R&& /*r*/) const
{}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
!detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value
&& detail::is_invocable<ResultCallback, R>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && cb, CallbackArg && /*cb_arg*/, R && r) const
{
detail::invoke(std::forward<ResultCallback>(cb), std::forward<R>(r));
}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
!detail::is_null_arg<ResultCallback>::value
&& !detail::is_null_arg<CallbackArg>::value
&& detail::is_invocable<ResultCallback, CallbackArg, R>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && cb, CallbackArg && cb_arg, R && r) const
{
detail::invoke(std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg), std::forward<R>(r));
}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
(!detail::is_null_arg<ResultCallback>::value
&& !detail::is_null_arg<CallbackArg>::value
&& !detail::is_invocable<ResultCallback, CallbackArg, R>::value)
|| (!detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value
&& !detail::is_invocable<ResultCallback, R>::value), int > = 0 >
void apply_invoke_cb(ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, R&& /*r*/) const
{
JSON_THROW(type_error::create(319, detail::concat("cannot invoke callback"), this));
}
template<bool ConstThis>
void apply_error() const
{
if (ConstThis)
{
JSON_THROW(type_error::create(318, detail::concat("cannot invoke callable with const JSON value of type ", type_name()), this));
}
JSON_THROW(type_error::create(318, detail::concat("cannot invoke callable with JSON value of type ", type_name()), this));
}
// invoke function and possibly delegate result
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < detail::apply_is_invocable<Value, Fn, Tuple, I...>::value
&& std::is_same<detail::apply_invoke_result_t<Value, Fn, Tuple, I...>, void>::value, int > = 0 >
void apply_invoke(Value && val, ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, Fn && f, Tuple && t, detail::index_sequence<I...> /*unused*/) const
{
detail::invoke(std::forward<Fn>(f), apply_resolve_placeholder(std::get<I>(t), std::forward<Value>(val))...);
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < detail::apply_is_invocable<Value, Fn, Tuple, I...>::value
&& !std::is_same<detail::apply_invoke_result_t<Value, Fn, Tuple, I...>, void>::value, int > = 0 >
void apply_invoke(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Tuple && t, detail::index_sequence<I...> /*unused*/) const
{
auto&& r = detail::invoke(std::forward<Fn>(f), apply_resolve_placeholder(std::get<I>(t), std::forward<Value>(val))...);
apply_invoke_cb(std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg), std::forward<decltype(r)>(r));
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < !detail::apply_is_invocable<Value, Fn, Tuple, I...>::value, int > = 0 >
void apply_invoke(Value && /*val*/, ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, Fn && /*f*/, Tuple && /*t*/, detail::index_sequence<I...> /*unused*/) const
{
apply_error<ConstThis>();
}
// convert arguments to tuple; insert basic_json_value placeholder if missing
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename FnArg, typename... Args,
detail::enable_if_t < std::is_member_pointer<Fn>::value
&& !detail::is_basic_json_value_placeholder<FnArg>::value
&& !detail::disjunction<detail::is_basic_json_value_placeholder<Args>...>::value, int > = 0 >
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, FnArg && f_arg, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(f_arg, ::nlohmann::placeholders::basic_json_value, args...),
detail::make_index_sequence < 2 + sizeof...(args) > ());
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args,
detail::enable_if_t < !std::is_member_pointer<Fn>::value
&& !detail::disjunction<detail::is_basic_json_value_placeholder<Args>...>::value, int > = 0 >
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(::nlohmann::placeholders::basic_json_value, args...),
detail::make_index_sequence < 1 + sizeof...(args) > ());
}
template<bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args,
detail::enable_if_t<detail::disjunction<detail::is_basic_json_value_placeholder<Args>...>::value, int> = 0>
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(args...),
detail::make_index_sequence<sizeof...(args)>());
}
// dispatch based on stored value type
template<bool ConstThis, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args>
void apply_dispatch(ResultCallback&& cb, CallbackArg&& cb_arg, Fn&& f, Args&& ... args) const
{
switch (m_type)
{
case value_t::null:
return apply_make_tuple<ConstThis>(nullptr,
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::object:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.object),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::array:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.array),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::string:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.string),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::boolean:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.boolean),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_integer:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_integer),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_unsigned:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_unsigned),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_float:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_float),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::binary:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.binary),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::discarded:
return apply_error<ConstThis>();
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
public:
template<typename Fn, typename... Args>
void apply(Fn&& f, Args&& ... args)
{
apply_dispatch<false>(
detail::null_arg, detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename Fn, typename... Args>
void apply(Fn&& f, Args&& ... args) const
{
apply_dispatch<true>(
detail::null_arg, detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t<
std::is_member_pointer<ResultCallback>::value, int> = 0>
void apply_cb(ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args)
{
apply_dispatch<false>(
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t<
std::is_member_pointer<ResultCallback>::value, int> = 0>
void apply_cb(ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_dispatch<true>(
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template < typename ResultCallback, typename Fn, typename... Args, detail::enable_if_t <
!std::is_member_pointer<ResultCallback>::value, int > = 0 >
void apply_cb(ResultCallback && cb, Fn && f, Args && ... args)
{
apply_dispatch<false>(
std::forward<ResultCallback>(cb), detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template < typename ResultCallback, typename Fn, typename... Args, detail::enable_if_t <
!std::is_member_pointer<ResultCallback>::value, int > = 0 >
void apply_cb(ResultCallback && cb, Fn && f, Args && ... args) const
{
apply_dispatch<true>(
std::forward<ResultCallback>(cb), detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename R, typename Fn, typename... Args>
R apply_r(Fn&& f, Args&& ... args)
{
R out;
apply_cb([&out](R && r) noexcept(noexcept(out = std::forward<decltype(r)>(r)))
{
out = std::forward<decltype(r)>(r);
}, std::forward<Fn>(f), std::forward<Args>(args)...);
return out;
}
template<typename R, typename Fn, typename... Args>
R apply_r(Fn&& f, Args&& ... args) const
{
R out;
apply_cb([&out](R && r) noexcept(noexcept(out = std::forward<decltype(r)>(r)))
{
out = std::forward<decltype(r)>(r);
}, std::forward<Fn>(f), std::forward<Args>(args)...);
return out;
}
};
#ifndef JSON_HAS_CPP_17
NLOHMANN_BASIC_JSON_TPL_DECLARATION
constexpr decltype(::nlohmann::placeholders::basic_json_value) NLOHMANN_BASIC_JSON_TPL::value_placeholder; // NOLINT(readability-redundant-declaration)
#endif
/// @brief user-defined to_string function for JSON values
/// @sa https://json.nlohmann.me/api/basic_json/to_string/
NLOHMANN_BASIC_JSON_TPL_DECLARATION

View File

@ -62,6 +62,7 @@ SOFTWARE.
#include <memory> // unique_ptr
#include <numeric> // accumulate
#include <string> // string, stoi, to_string
#include <tuple> // forward_as_tuple, tuple
#include <utility> // declval, forward, move, pair, swap
#include <vector> // vector
@ -3023,6 +3024,46 @@ using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
#endif
#ifdef JSON_HAS_CPP_17
// the following utilities are natively available in C++17
using std::conjunction;
using std::disjunction;
using std::negation;
using std::as_const;
#else
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type {};
template<class B> struct conjunction<B> : B {};
template<class B, class... Bn>
struct conjunction<B, Bn...>
: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
// https://en.cppreference.com/w/cpp/types/disjunction
template<class...> struct disjunction : std::false_type {};
template<class B> struct disjunction<B> : B {};
template<class B, class... Bn>
struct disjunction<B, Bn...>
: std::conditional<bool(B::value), B, disjunction<Bn...>>::type {};
// https://en.cppreference.com/w/cpp/types/negation
template<class B> struct negation : std::integral_constant < bool, !B::value > {};
// https://en.cppreference.com/w/cpp/utility/as_const
template <typename T>
constexpr typename std::add_const<T>::type& as_const(T& t) noexcept
{
return t;
}
template <typename T>
void as_const(const T&&) = delete;
#endif
// dispatch utility (taken from ranges-v3)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
template<> struct priority_tag<0> {};
@ -3136,6 +3177,32 @@ NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
// #include <nlohmann/detail/meta/detected.hpp>
// #include <nlohmann/detail/placeholders.hpp>
namespace nlohmann
{
namespace detail
{
template<int I>
struct placeholder_t
{
static constexpr int value = I;
explicit placeholder_t() = default;
};
} // namespace detail
namespace placeholders
{
static constexpr detail::placeholder_t < -1 > basic_json_value{};
} // namespace placeholders
} // namespace nlohmann
// #include <nlohmann/json_fwd.hpp>
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
@ -3367,16 +3434,6 @@ using actual_object_comparator_t = typename actual_object_comparator<BasicJsonTy
// is_ functions //
///////////////////
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type { };
template<class B> struct conjunction<B> : B { };
template<class B, class... Bn>
struct conjunction<B, Bn...>
: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
// https://en.cppreference.com/w/cpp/types/negation
template<class B> struct negation : std::integral_constant < bool, !B::value > { };
// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
@ -3753,6 +3810,11 @@ struct is_ordered_map
enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
};
// checks if a type is a basic_json_value placeholder
template<typename T>
using is_basic_json_value_placeholder = typename std::is_same <
uncvref_t<T>, uncvref_t<decltype(::nlohmann::placeholders::basic_json_value) >>::type;
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
T conditional_static_cast(U value)
@ -3766,6 +3828,18 @@ T conditional_static_cast(U value)
return value;
}
// non-standard conditional version of as_const
template <bool AsConst, typename T>
constexpr typename std::conditional<AsConst,
typename std::add_const<T>::type, T>::type&
conditional_as_const(T& t) noexcept
{
return t;
}
template <bool AsConst, typename T>
void conditional_as_const(const T&&) = delete;
} // namespace detail
} // namespace nlohmann
@ -13953,6 +14027,131 @@ class json_ref
// #include <nlohmann/detail/meta/cpp_future.hpp>
// #include <nlohmann/detail/meta/invoke.hpp>
#ifdef JSON_HAS_CPP_17
#include <functional> // invoke
#endif
#include <type_traits> // invoke_result, is_base_of, is_function, is_invocable, is_object, is_same, remove_reference
#include <utility> // declval, forward
// #include <nlohmann/detail/macro_scope.hpp>
// #include <nlohmann/detail/meta/detected.hpp>
// #include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
#ifdef JSON_HAS_CPP_17
// the following utilities are natively available in C++17
using std::invoke;
using std::invoke_result;
using std::is_invocable;
#else
// invoke_impl derived from the C++ standard draft [func.require] for qslib (proprietary)
// modified to use standard library type traits and utilities where possible
namespace internal
{
template<typename Fn, typename... Args,
typename = decltype(std::declval<Fn>()(std::forward<Args>(std::declval<Args>())...))>
auto invoke_impl(Fn && f, Args && ...args) -> decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
};
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_function<T>::value
&& std::is_base_of<U, typename std::remove_reference<V>::type>::value, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ...args) -> decltype((v.*f)(std::forward<Args>(args)...))
{
return (v.*f)(std::forward<Args>(args)...);
}
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_function<T>::value
&& !std::is_base_of<U, typename std::remove_reference<V>::type>::value, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ...args) -> decltype(((*v).*f)(std::forward<Args>(args)...))
{
return ((*v).*f)(std::forward<Args>(args)...);
}
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_object<T>::value
&& sizeof...(Args) == 0, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ... /*unused*/) -> decltype((*v).*f)
{
return (*v).*f;
}
template <typename Fn, typename... Args>
using detect_invocable = decltype(invoke_impl(std::declval<Fn>(), std::declval<Args>()...));
// https://en.cppreference.com/w/cpp/types/result_of
template <typename AlwaysVoid, typename, typename...>
struct invoke_result {};
template <typename Fn, typename...Args>
struct invoke_result<decltype(void(invoke_impl(std::declval<Fn>(), std::declval<Args>()...))), Fn, Args...>
{
using type = decltype(invoke_impl(std::declval<Fn>(), std::declval<Args>()...));
};
} // namespace internal
template <typename Fn, typename... Args>
auto invoke(Fn&& f, Args&& ... args) -> decltype(internal::invoke_impl(std::forward<Fn>(f), std::forward<Args>(args)...))
{
return internal::invoke_impl(std::forward<Fn>(f), std::forward<Args>(args)...);
}
template <typename Fn, typename... Args>
using invoke_result = internal::invoke_result<void, Fn, Args...>;
template <typename Fn, typename... Args>
using is_invocable = typename is_detected<internal::detect_invocable, Fn, Args...>::type;
#endif
// used as a dummy argument
struct null_arg_t
{
explicit null_arg_t() = default;
};
static constexpr null_arg_t null_arg{};
template<typename T>
using is_null_arg = typename std::is_same<uncvref_t<T>, null_arg_t>::type;
template<typename Value, typename Tuple, std::size_t I>
struct apply_value_or_arg
{
using element_type = typename std::tuple_element<I, Tuple>::type;
using type = typename std::conditional<detail::is_basic_json_value_placeholder<element_type>::value, Value, element_type>::type;
};
template<typename Value, typename Fn, typename Tuple, std::size_t... I>
using apply_invoke_result_t = typename detail::invoke_result<Fn,
typename apply_value_or_arg<Value, Tuple, I>::type...>::type;
template<typename Value, typename Fn, typename Tuple, std::size_t... I>
using apply_is_invocable = typename detail::is_invocable<Fn,
typename apply_value_or_arg<Value, Tuple, I>::type...>::type;
} // namespace detail
} // namespace nlohmann
// #include <nlohmann/detail/meta/type_traits.hpp>
// #include <nlohmann/detail/output/binary_writer.hpp>
@ -18030,6 +18229,8 @@ class serializer
} // namespace detail
} // namespace nlohmann
// #include <nlohmann/detail/placeholders.hpp>
// #include <nlohmann/detail/value_t.hpp>
// #include <nlohmann/json_fwd.hpp>
@ -18389,6 +18590,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// SAX interface type, see @ref nlohmann::json_sax
using json_sax_t = json_sax<basic_json>;
// placeholder for the stored JSON value used in apply*() functions
static constexpr decltype(::nlohmann::placeholders::basic_json_value) value_placeholder
= ::nlohmann::placeholders::basic_json_value;
////////////////
// exceptions //
////////////////
@ -23302,8 +23507,267 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
/// @}
private:
template<typename Arg, typename Value,
detail::enable_if_t<detail::is_basic_json_value_placeholder<Arg>::value, int> = 0>
static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward<Value>(val))
{
return std::forward<Value>(val);
}
template < typename Arg, typename Value,
detail::enable_if_t < !detail::is_basic_json_value_placeholder<Arg>::value, int > = 0 >
static auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) -> decltype(std::forward<Arg>(arg))
{
return std::forward<Arg>(arg);
}
// invoke the result callback
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, R&& /*r*/) const
{}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
!detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value
&& detail::is_invocable<ResultCallback, R>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && cb, CallbackArg && /*cb_arg*/, R && r) const
{
detail::invoke(std::forward<ResultCallback>(cb), std::forward<R>(r));
}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
!detail::is_null_arg<ResultCallback>::value
&& !detail::is_null_arg<CallbackArg>::value
&& detail::is_invocable<ResultCallback, CallbackArg, R>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && cb, CallbackArg && cb_arg, R && r) const
{
detail::invoke(std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg), std::forward<R>(r));
}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
(!detail::is_null_arg<ResultCallback>::value
&& !detail::is_null_arg<CallbackArg>::value
&& !detail::is_invocable<ResultCallback, CallbackArg, R>::value)
|| (!detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value
&& !detail::is_invocable<ResultCallback, R>::value), int > = 0 >
void apply_invoke_cb(ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, R&& /*r*/) const
{
JSON_THROW(type_error::create(319, detail::concat("cannot invoke callback"), this));
}
template<bool ConstThis>
void apply_error() const
{
if (ConstThis)
{
JSON_THROW(type_error::create(318, detail::concat("cannot invoke callable with const JSON value of type ", type_name()), this));
}
JSON_THROW(type_error::create(318, detail::concat("cannot invoke callable with JSON value of type ", type_name()), this));
}
// invoke function and possibly delegate result
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < detail::apply_is_invocable<Value, Fn, Tuple, I...>::value
&& std::is_same<detail::apply_invoke_result_t<Value, Fn, Tuple, I...>, void>::value, int > = 0 >
void apply_invoke(Value && val, ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, Fn && f, Tuple && t, detail::index_sequence<I...> /*unused*/) const
{
detail::invoke(std::forward<Fn>(f), apply_resolve_placeholder(std::get<I>(t), std::forward<Value>(val))...);
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < detail::apply_is_invocable<Value, Fn, Tuple, I...>::value
&& !std::is_same<detail::apply_invoke_result_t<Value, Fn, Tuple, I...>, void>::value, int > = 0 >
void apply_invoke(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Tuple && t, detail::index_sequence<I...> /*unused*/) const
{
auto&& r = detail::invoke(std::forward<Fn>(f), apply_resolve_placeholder(std::get<I>(t), std::forward<Value>(val))...);
apply_invoke_cb(std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg), std::forward<decltype(r)>(r));
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < !detail::apply_is_invocable<Value, Fn, Tuple, I...>::value, int > = 0 >
void apply_invoke(Value && /*val*/, ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, Fn && /*f*/, Tuple && /*t*/, detail::index_sequence<I...> /*unused*/) const
{
apply_error<ConstThis>();
}
// convert arguments to tuple; insert basic_json_value placeholder if missing
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename FnArg, typename... Args,
detail::enable_if_t < std::is_member_pointer<Fn>::value
&& !detail::is_basic_json_value_placeholder<FnArg>::value
&& !detail::disjunction<detail::is_basic_json_value_placeholder<Args>...>::value, int > = 0 >
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, FnArg && f_arg, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(f_arg, ::nlohmann::placeholders::basic_json_value, args...),
detail::make_index_sequence < 2 + sizeof...(args) > ());
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args,
detail::enable_if_t < !std::is_member_pointer<Fn>::value
&& !detail::disjunction<detail::is_basic_json_value_placeholder<Args>...>::value, int > = 0 >
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(::nlohmann::placeholders::basic_json_value, args...),
detail::make_index_sequence < 1 + sizeof...(args) > ());
}
template<bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args,
detail::enable_if_t<detail::disjunction<detail::is_basic_json_value_placeholder<Args>...>::value, int> = 0>
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(args...),
detail::make_index_sequence<sizeof...(args)>());
}
// dispatch based on stored value type
template<bool ConstThis, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args>
void apply_dispatch(ResultCallback&& cb, CallbackArg&& cb_arg, Fn&& f, Args&& ... args) const
{
switch (m_type)
{
case value_t::null:
return apply_make_tuple<ConstThis>(nullptr,
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::object:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.object),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::array:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.array),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::string:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.string),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::boolean:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.boolean),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_integer:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_integer),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_unsigned:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_unsigned),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_float:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_float),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::binary:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.binary),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::discarded:
return apply_error<ConstThis>();
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
public:
template<typename Fn, typename... Args>
void apply(Fn&& f, Args&& ... args)
{
apply_dispatch<false>(
detail::null_arg, detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename Fn, typename... Args>
void apply(Fn&& f, Args&& ... args) const
{
apply_dispatch<true>(
detail::null_arg, detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t<
std::is_member_pointer<ResultCallback>::value, int> = 0>
void apply_cb(ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args)
{
apply_dispatch<false>(
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t<
std::is_member_pointer<ResultCallback>::value, int> = 0>
void apply_cb(ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_dispatch<true>(
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template < typename ResultCallback, typename Fn, typename... Args, detail::enable_if_t <
!std::is_member_pointer<ResultCallback>::value, int > = 0 >
void apply_cb(ResultCallback && cb, Fn && f, Args && ... args)
{
apply_dispatch<false>(
std::forward<ResultCallback>(cb), detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template < typename ResultCallback, typename Fn, typename... Args, detail::enable_if_t <
!std::is_member_pointer<ResultCallback>::value, int > = 0 >
void apply_cb(ResultCallback && cb, Fn && f, Args && ... args) const
{
apply_dispatch<true>(
std::forward<ResultCallback>(cb), detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename R, typename Fn, typename... Args>
R apply_r(Fn&& f, Args&& ... args)
{
R out;
apply_cb([&out](R && r) noexcept(noexcept(out = std::forward<decltype(r)>(r)))
{
out = std::forward<decltype(r)>(r);
}, std::forward<Fn>(f), std::forward<Args>(args)...);
return out;
}
template<typename R, typename Fn, typename... Args>
R apply_r(Fn&& f, Args&& ... args) const
{
R out;
apply_cb([&out](R && r) noexcept(noexcept(out = std::forward<decltype(r)>(r)))
{
out = std::forward<decltype(r)>(r);
}, std::forward<Fn>(f), std::forward<Args>(args)...);
return out;
}
};
#ifndef JSON_HAS_CPP_17
NLOHMANN_BASIC_JSON_TPL_DECLARATION
constexpr decltype(::nlohmann::placeholders::basic_json_value) NLOHMANN_BASIC_JSON_TPL::value_placeholder; // NOLINT(readability-redundant-declaration)
#endif
/// @brief user-defined to_string function for JSON values
/// @sa https://json.nlohmann.me/api/basic_json/to_string/
NLOHMANN_BASIC_JSON_TPL_DECLARATION

661
tests/src/unit-apply.cpp Normal file
View File

@ -0,0 +1,661 @@
#include "doctest_compatibility.h"
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-conversion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wimplicit-int-float-conversion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-conversion")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
DOCTEST_MSVC_SUPPRESS_WARNING(4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data
DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <cstdint>
#if JSON_HAS_RANGES
// JSON_HAS_CPP_20 (magic keyword; do not remove)
#include <ranges>
#endif
// MSSTL defines as_const in the global namespace :facepalm:
template<typename... Args>
static auto const_(Args&& ... args) -> decltype(nlohmann::detail::as_const(std::forward<Args>(args)...))
{
return nlohmann::detail::as_const(std::forward<Args>(args)...);
}
static void array_push_back(json::array_t& arr, json val)
{
arr.emplace_back(std::move(val));
}
static void array_push_front(json val, json::array_t& arr)
{
arr.emplace(arr.begin(), std::move(val));
}
struct foo
{
int bar = 0;
void set_bar(int i) noexcept
{
bar = i;
}
static int static_bar;
static void static_set_bar(int i) noexcept
{
static_bar = i;
}
};
int foo::static_bar = 0;
struct functor
{
int arg;
int value = 0;
explicit functor(int arg_ = 0) noexcept : arg(arg_) {}
void operator()(int a, int b = 0, int c = 0) noexcept
{
switch (arg)
{
default:
case 0:
value = a;
break;
case 1:
value = b;
break;
case 2:
value = c;
break;
}
}
};
static int get_value(int i) noexcept
{
return i;
}
static int callback_value = 0;
static void callback(int i) noexcept
{
callback_value = i;
}
struct not_an_int
{
explicit not_an_int() = default;
};
static not_an_int get_not_an_int(int /*unused*/)
{
return not_an_int{};
}
TEST_CASE("apply*() functions")
{
SECTION("placeholder")
{
using nlohmann::placeholders::basic_json_value;
using nlohmann::detail::is_basic_json_value_placeholder;
CHECK(std::is_same<decltype(basic_json_value), decltype(json::value_placeholder)>::value);
CHECK(is_basic_json_value_placeholder<decltype(json::value_placeholder)>::value);
CHECK_FALSE(is_basic_json_value_placeholder<json>::value);
}
SECTION("apply()")
{
SECTION("plain function")
{
SECTION("const")
{
const json j = json::array({"foo"});
CHECK_THROWS_WITH_AS(j.apply(array_push_back, 42),
"[json.exception.type_error.318] cannot invoke callable with const JSON value of type array",
json::type_error&);
}
SECTION("non-const")
{
SECTION("without explicit placeholder")
{
json j = json::array({"foo"});
json j_expected = json::array({"foo", 42});
j.apply(array_push_back, 42);
CHECK(j == j_expected);
}
SECTION("with explicit placeholder")
{
json j = json::array({"foo"});
json j_expected = json::array({42, "foo"});
json j_expected2 = json::array({42, "foo", 24});
j.apply(array_push_front, 42, json::value_placeholder);
CHECK(j == j_expected);
j.apply(array_push_back, json::value_placeholder, 24);
CHECK(j == j_expected2);
}
}
}
SECTION("static member function pointer")
{
json j(42);
SECTION("const (without explicit placeholder)")
{
foo::static_bar = 0;
const_(j).apply(&foo::static_set_bar);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
SECTION("const (with explicit placeholder)")
{
foo::static_bar = 0;
const_(j).apply(&foo::static_set_bar, json::value_placeholder);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
SECTION("non-const (without explicit placeholder)")
{
foo::static_bar = 0;
j.apply(&foo::static_set_bar);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
SECTION("non-const (with explicit placeholder)")
{
foo::static_bar = 0;
j.apply(&foo::static_set_bar, json::value_placeholder);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
}
SECTION("non-static member function pointer")
{
json j(42);
SECTION("const (without explicit placeholder)")
{
foo f;
const_(j).apply(&foo::set_bar, f);
CHECK(f.bar == 42);
CHECK(j == 42);
}
SECTION("const (with explicit placeholder)")
{
foo f;
const_(j).apply(&foo::set_bar, f, json::value_placeholder);
CHECK(f.bar == 42);
CHECK(j == 42);
}
SECTION("non-const (without explicit placeholder)")
{
foo f;
j.apply(&foo::set_bar, f);
CHECK(f.bar == 42);
CHECK(j == 42);
}
SECTION("non-const (with explicit placeholder)")
{
foo f;
j.apply(&foo::set_bar, f, json::value_placeholder);
CHECK(f.bar == 42);
CHECK(j == 42);
}
}
SECTION("non-static function member pointer (json::array_t::resize)")
{
json j = json::array();
json j_expected = json::array({42, 42});
SECTION("const")
{
CHECK_THROWS_WITH_AS(const_(j).apply(static_cast<void (json::array_t::*)(json::array_t::size_type, const json&)>(&json::array_t::resize), json::value_placeholder, 2, json(42)),
"[json.exception.type_error.318] cannot invoke callable with const JSON value of type array",
json::type_error&);
CHECK(j.empty());
}
SECTION("non-const (without explicit placeholder)")
{
CHECK_THROWS_WITH_AS(
j.apply(static_cast<void (json::array_t::*)(json::array_t::size_type, const json&)>(&json::array_t::resize), 2, json(42)),
"[json.exception.type_error.318] cannot invoke callable with JSON value of type array",
json::type_error&);
CHECK(j.empty());
}
SECTION("non-const (with explicit placeholder)")
{
j.apply(static_cast<void (json::array_t::*)(json::array_t::size_type, const json&)>(&json::array_t::resize), json::value_placeholder, 2, json(42));
CHECK(j == j_expected);
}
}
SECTION("functor")
{
json j(42);
SECTION("const (without explicit placeholder)")
{
functor f{0};
const_(j).apply(f, -1, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
SECTION("const (with explicit placeholder)")
{
functor f{1};
const_(j).apply(f, 0, json::value_placeholder, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
SECTION("non-const (without explicit placeholder)")
{
functor f{0};
j.apply(f, -1, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
SECTION("non-const (with explicit placeholder)")
{
functor f{1};
j.apply(f, 0, json::value_placeholder, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
}
SECTION("discarded JSON value")
{
json j(json::value_t::discarded);
SECTION("const")
{
CHECK_THROWS_WITH_AS(
const_(j).apply(nlohmann::detail::null_arg),
"[json.exception.type_error.318] cannot invoke callable with const JSON value of type discarded",
json::type_error&);
}
SECTION("non-const")
{
CHECK_THROWS_WITH_AS(
j.apply(nlohmann::detail::null_arg),
"[json.exception.type_error.318] cannot invoke callable with JSON value of type discarded",
json::type_error&);
}
}
}
// apply_cb() (which apply_r() is based on) fails on Clang
// due to is_invocable not yielding true as expected
// while testing the invocable implementation Clang 3.5 crashed
#if !defined(__clang__) || (defined(__clang__) && __clang_major__ == 3 && __clang_minor__ < 6)
SECTION("apply_r()")
{
SECTION("value types")
{
SECTION("null")
{
json j;
auto is_null = [](std::nullptr_t) noexcept
{
return true;
};
SECTION("const")
{
CHECK(j.is_null());
CHECK(const_(j).apply_r<bool>(is_null));
}
SECTION("non-const")
{
CHECK(j.is_null());
CHECK(j.apply_r<bool>(is_null));
}
}
SECTION("object")
{
json j{{"foo", 0}, {"bar", 42}};
auto get_bar = [](const json::object_t& obj)
{
#if JSON_USE_IMPLICIT_CONVERSIONS
return obj.at("bar");
#else
return obj.at("bar").get<int>();
#endif
};
SECTION("const")
{
CHECK(j.is_object());
CHECK(const_(j).apply_r<int>(get_bar) == 42);
}
SECTION("non-const")
{
CHECK(j.is_object());
CHECK(j.apply_r<int>(get_bar) == 42);
}
}
SECTION("array")
{
json j{0, 1, 42, 3, 4};
auto get_2 = [](const json::array_t& arr)
{
#if JSON_USE_IMPLICIT_CONVERSIONS
return arr[2];
#else
return arr[2].get<int>();
#endif
};
SECTION("const")
{
CHECK(j.is_array());
CHECK(const_(j).apply_r<int>(get_2) == 42);
}
SECTION("non-const")
{
CHECK(j.is_array());
CHECK(j.apply_r<int>(get_2) == 42);
}
}
SECTION("string")
{
json j("fourty two");
auto length = [](const json::string_t& str) noexcept
{
return str.size();
};
SECTION("const")
{
CHECK(j.is_string());
CHECK(const_(j).apply_r<std::size_t>(length) == 10);
}
SECTION("non-const")
{
CHECK(j.is_string());
CHECK(j.apply_r<std::size_t>(length) == 10);
}
}
SECTION("boolean")
{
json j(false);
auto negate = [](bool b) noexcept
{
return !b;
};
SECTION("const")
{
CHECK(j.is_boolean());
CHECK(const_(j).apply_r<bool>(negate));
}
SECTION("non-const")
{
CHECK(j.is_boolean());
CHECK(j.apply_r<bool>(negate));
}
}
SECTION("number_integer")
{
json j(-7);
auto calc = [](json::number_integer_t i) noexcept
{
return i * i + i;
};
SECTION("const")
{
CHECK(j.is_number_integer());
CHECK(const_(j).apply_r<int>(calc) == 42);
}
SECTION("non-const")
{
CHECK(j.is_number_integer());
CHECK(j.apply_r<int>(calc) == 42);
}
}
SECTION("number_unsigned")
{
json j(static_cast<json::number_unsigned_t>(7));
auto calc = [](json::number_unsigned_t i) noexcept
{
return i * i - i;
};
SECTION("const")
{
CHECK(j.is_number_unsigned());
CHECK(const_(j).apply_r<int>(calc) == 42);
}
SECTION("non-const")
{
CHECK(j.is_number_unsigned());
CHECK(j.apply_r<int>(calc) == 42);
}
}
SECTION("number_float")
{
json j(6.480741);
auto square = [](json::number_float_t f) noexcept
{
return f * f;
};
SECTION("const")
{
CHECK(j.is_number_float());
CHECK(const_(j).apply_r<double>(square) == doctest::Approx(42.0));
}
SECTION("non-const")
{
CHECK(j.is_number_float());
CHECK(j.apply_r<double>(square) == doctest::Approx(42.0));
}
}
SECTION("binary")
{
json j = json::binary(std::vector<std::uint8_t> {0xC0, 0xFF, 0xEE});
auto get_1 = [](const json::binary_t& bin) noexcept
{
return bin[1];
};
SECTION("const")
{
CHECK(j.is_binary());
CHECK(const_(j).apply_r<std::uint8_t>(get_1) == 0xFF);
}
SECTION("non-const")
{
CHECK(j.is_binary());
CHECK(j.apply_r<std::uint8_t>(get_1) == 0xFF);
}
}
}
#if JSON_HAS_RANGES
SECTION("std::ranges::min")
{
json j = json::array({5, 3, 4, 2});
SECTION("const (without explicit placeholder)")
{
CHECK(const_(j).apply_r<int>(std::ranges::min) == 2);
}
SECTION("const (with explicit placeholder)")
{
CHECK(const_(j).apply_r<int>(std::ranges::min, json::value_placeholder) == 2);
}
SECTION("non-const (without explicit placeholder)")
{
CHECK(j.apply_r<int>(std::ranges::min) == 2);
}
SECTION("non-const (with explicit placeholder)")
{
CHECK(j.apply_r<int>(std::ranges::min, json::value_placeholder) == 2);
}
}
#endif
}
SECTION("apply_cb()")
{
SECTION("plain function callback")
{
json j(42);
SECTION("const")
{
callback_value = 0;
const_(j).apply_cb(callback, get_value);
CHECK(callback_value == 42);
}
SECTION("non-const")
{
callback_value = 0;
j.apply_cb(callback, get_value);
CHECK(callback_value == 42);
}
SECTION("exception")
{
CHECK_THROWS_WITH_AS(
j.apply_cb(callback, get_not_an_int),
"[json.exception.type_error.319] cannot invoke callback",
json::type_error&);
}
}
SECTION("static member function pointer")
{
json j(42);
SECTION("const")
{
foo::static_bar = 0;
const_(j).apply_cb(&foo::static_set_bar, get_value);
CHECK(foo::static_bar == 42);
}
SECTION("non-const")
{
foo::static_bar = 0;
j.apply_cb(&foo::static_set_bar, get_value);
CHECK(foo::static_bar == 42);
}
}
SECTION("non-static member function pointer")
{
json j(42);
SECTION("const")
{
foo f;
const_(j).apply_cb(&foo::set_bar, f, get_value);
CHECK(f.bar == 42);
}
SECTION("non-const")
{
foo f;
j.apply_cb(&foo::set_bar, f, get_value);
CHECK(f.bar == 42);
}
}
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP