From 7fb7fc89ef587f6b54b5d1c317a62d078010efa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Delrieu?= Date: Thu, 16 Sep 2021 16:24:38 +0200 Subject: [PATCH] meta: fix is_compatible/constructible traits The previous version relied on the existence of an 'iterator' type. As mentioned in comments, this is not the proper way to do it and causes issues with certain types (e.g. views from range-v3). Add a 'is_range' trait that properly detects the return type of 'begin'/'end', and use it in instead. --- include/nlohmann/detail/macro_scope.hpp | 38 +- include/nlohmann/detail/macro_unscope.hpp | 2 + .../nlohmann/detail/meta/call_std/begin.hpp | 8 + include/nlohmann/detail/meta/call_std/end.hpp | 8 + include/nlohmann/detail/meta/can_call_std.hpp | 37 ++ include/nlohmann/detail/meta/type_traits.hpp | 314 ++++++---- single_include/nlohmann/json.hpp | 539 ++++++++++++------ test/src/unit-readme.cpp | 77 ++- test/src/unit-udt.cpp | 185 +++--- 9 files changed, 792 insertions(+), 416 deletions(-) create mode 100644 include/nlohmann/detail/meta/call_std/begin.hpp create mode 100644 include/nlohmann/detail/meta/call_std/end.hpp create mode 100644 include/nlohmann/detail/meta/can_call_std.hpp diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 76a0dc62cf..b815524727 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -1,7 +1,8 @@ #pragma once -#include // pair +#include // declval, pair #include +#include // This file contains all internal macro definitions // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them @@ -292,6 +293,41 @@ inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +// source: https://stackoverflow.com/a/26745591 + +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + #ifndef JSON_USE_IMPLICIT_CONVERSIONS #define JSON_USE_IMPLICIT_CONVERSIONS 1 #endif diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index 3f56e4b85e..1da0af98eb 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -19,5 +19,7 @@ #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT +#undef JSON_CATCH +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL #include diff --git a/include/nlohmann/detail/meta/call_std/begin.hpp b/include/nlohmann/detail/meta/call_std/begin.hpp new file mode 100644 index 0000000000..cc34974dd7 --- /dev/null +++ b/include/nlohmann/detail/meta/call_std/begin.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} diff --git a/include/nlohmann/detail/meta/call_std/end.hpp b/include/nlohmann/detail/meta/call_std/end.hpp new file mode 100644 index 0000000000..071378ff10 --- /dev/null +++ b/include/nlohmann/detail/meta/call_std/end.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} diff --git a/include/nlohmann/detail/meta/can_call_std.hpp b/include/nlohmann/detail/meta/can_call_std.hpp new file mode 100644 index 0000000000..6f94b6b285 --- /dev/null +++ b/include/nlohmann/detail/meta/can_call_std.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +// source: https://stackoverflow.com/a/26745591 + +#define CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index ede55acc26..8a8ab4bcc2 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -1,12 +1,15 @@ #pragma once -#include // numeric_limits -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval -#include // tuple +#include // numeric_limits +#include // tuple +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval -#include #include + +#include +#include +#include #include #include #include @@ -36,10 +39,13 @@ namespace detail // In this case, T has to be properly CV-qualified to constraint the function arguments // (e.g. to_json(BasicJsonType&, const T&)) -template struct is_basic_json : std::false_type {}; +template +struct is_basic_json : std::false_type +{}; NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json : std::true_type {}; +struct is_basic_json : std::true_type +{}; ////////////////////// // json_ref helpers // @@ -49,10 +55,12 @@ template class json_ref; template -struct is_json_ref : std::false_type {}; +struct is_json_ref : std::false_type +{}; template -struct is_json_ref> : std::true_type {}; +struct is_json_ref> : std::true_type +{}; ////////////////////////// // aliases for detected // @@ -79,9 +87,6 @@ using reference_t = typename T::reference; template using iterator_category_t = typename T::iterator_category; -template -using iterator_t = typename T::iterator; - template using to_json_function = decltype(T::to_json(std::declval()...)); @@ -93,13 +98,14 @@ using get_template_function = decltype(std::declval().template get()); // trait checking if JSONSerializer::from_json(json const&, udt&) exists template -struct has_from_json : std::false_type {}; +struct has_from_json : std::false_type +{}; // trait checking if j.get is valid // use this trait instead of std::is_constructible or std::is_convertible, // both rely on, or make use of implicit conversions, and thus fail when T // has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) -template +template struct is_getable { static constexpr bool value = is_detected::value; @@ -111,14 +117,14 @@ struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = - is_detected_exact::value; + is_detected_exact::value; }; // This trait checks if JSONSerializer::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types template -struct has_non_default_from_json : std::false_type {}; +struct has_non_default_from_json : std::false_type +{}; template struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> @@ -126,14 +132,14 @@ struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_jso using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = - is_detected_exact::value; + is_detected_exact::value; }; // This trait checks if BasicJsonType::json_serializer::to_json exists // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. template -struct has_to_json : std::false_type {}; +struct has_to_json : std::false_type +{}; template struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> @@ -141,66 +147,80 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = - is_detected_exact::value; + is_detected_exact::value; }; - /////////////////// // is_ functions // /////////////////// // https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; +template +struct conjunction : std::true_type +{}; +template +struct conjunction : B1 +{}; template struct conjunction -: std::conditional, B1>::type {}; +: std::conditional, B1>::type + {}; // https://en.cppreference.com/w/cpp/types/negation -template struct negation : std::integral_constant < bool, !B::value > { }; +template +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. -template -struct is_default_constructible : std::is_default_constructible {}; +template +struct is_default_constructible : std::is_default_constructible +{}; -template +template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> +{}; -template +template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> +{}; -template +template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> +{}; -template +template struct is_default_constructible> - : conjunction...> {}; - - -template -struct is_constructible : std::is_constructible {}; + : conjunction...> +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible : std::is_constructible +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible> : is_default_constructible> +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible> : is_default_constructible> +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible> : is_default_constructible> +{}; +template +struct is_constructible> : is_default_constructible> +{}; template -struct is_iterator_traits : std::false_type {}; +struct is_iterator_traits : std::false_type +{}; template struct is_iterator_traits> @@ -217,23 +237,54 @@ struct is_iterator_traits> is_detected::value; }; +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + static constexpr auto has_begin = !std::is_same::value; + static constexpr auto has_end = !std::is_same::value; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = has_begin && has_end && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + // The following implementation of is_complete_type is taken from // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ // and is written by Xiang Fan who agreed to using it in this library. template -struct is_complete_type : std::false_type {}; +struct is_complete_type : std::false_type +{}; template -struct is_complete_type : std::true_type {}; +struct is_complete_type : std::true_type +{}; -template -struct is_compatible_object_type_impl : std::false_type {}; +template +struct is_compatible_object_type_impl : std::false_type +{}; template struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, + BasicJsonType, + CompatibleObjectType, enable_if_t < is_detected::value&& is_detected::value >> { @@ -249,15 +300,17 @@ struct is_compatible_object_type_impl < template struct is_compatible_object_type - : is_compatible_object_type_impl {}; + : is_compatible_object_type_impl +{}; -template -struct is_constructible_object_type_impl : std::false_type {}; +template +struct is_constructible_object_type_impl : std::false_type +{}; template struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, + BasicJsonType, + ConstructibleObjectType, enable_if_t < is_detected::value&& is_detected::value >> { @@ -282,17 +335,20 @@ struct is_constructible_object_type_impl < template struct is_constructible_object_type : is_constructible_object_type_impl {}; + ConstructibleObjectType> +{}; -template -struct is_compatible_string_type_impl : std::false_type {}; +template +struct is_compatible_string_type_impl : std::false_type +{}; template struct is_compatible_string_type_impl < - BasicJsonType, CompatibleStringType, - enable_if_t::value >> + BasicJsonType, + CompatibleStringType, + enable_if_t::value >> { static constexpr auto value = is_constructible::value; @@ -300,17 +356,20 @@ struct is_compatible_string_type_impl < template struct is_compatible_string_type - : is_compatible_string_type_impl {}; + : is_compatible_string_type_impl +{}; -template -struct is_constructible_string_type_impl : std::false_type {}; +template +struct is_constructible_string_type_impl : std::false_type +{}; template struct is_constructible_string_type_impl < - BasicJsonType, ConstructibleStringType, + BasicJsonType, + ConstructibleStringType, enable_if_t::value >> + value_type_t, + ConstructibleStringType>::value >> { static constexpr auto value = is_constructible struct is_constructible_string_type - : is_constructible_string_type_impl {}; + : is_constructible_string_type_impl +{}; template -struct is_compatible_array_type_impl : std::false_type {}; +struct is_compatible_array_type_impl : std::false_type +{}; template struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < is_detected::value&& + BasicJsonType, + CompatibleArrayType, + enable_if_t < is_detected::value&& -// This is needed because json_reverse_iterator has a ::iterator type... -// Therefore it is detected as a CompatibleArrayType. -// The real fix would be to have an Iterable concept. - !is_iterator_traits < - iterator_traits>::value >> + is_iterator_traits>>::value >> { static constexpr bool value = is_constructible::value; + range_value_t>::value; }; template struct is_compatible_array_type - : is_compatible_array_type_impl {}; + : is_compatible_array_type_impl +{}; template -struct is_constructible_array_type_impl : std::false_type {}; +struct is_constructible_array_type_impl : std::false_type +{}; template struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, + BasicJsonType, + ConstructibleArrayType, enable_if_t::value >> - : std::true_type {}; + : std::true_type +{}; template struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, + BasicJsonType, + ConstructibleArrayType, enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& is_default_constructible::value&& (std::is_move_assignable::value || std::is_copy_assignable::value)&& -is_detected::value&& is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& is_complete_type < -detected_t>::value >> +detected_t>::value >> { + using value_type = range_value_t; + static constexpr bool value = - // This is needed because json_reverse_iterator has a ::iterator type, - // furthermore, std::back_insert_iterator (and other iterators) have a - // base class `iterator`... Therefore it is detected as a - // ConstructibleArrayType. The real fix would be to have an Iterable - // concept. - !is_iterator_traits>::value && - - (std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, typename ConstructibleArrayType::value_type >::value); + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; }; template struct is_constructible_array_type - : is_constructible_array_type_impl {}; + : is_constructible_array_type_impl +{}; -template -struct is_compatible_integer_type_impl : std::false_type {}; +template +struct is_compatible_integer_type_impl : std::false_type +{}; template struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, + RealIntegerType, + CompatibleNumberIntegerType, enable_if_t < std::is_integral::value&& std::is_integral::value&& !std::is_same::value >> @@ -412,14 +475,17 @@ struct is_compatible_integer_type_impl < template struct is_compatible_integer_type : is_compatible_integer_type_impl {}; + CompatibleNumberIntegerType> +{}; template -struct is_compatible_type_impl: std::false_type {}; +struct is_compatible_type_impl : std::false_type +{}; template struct is_compatible_type_impl < - BasicJsonType, CompatibleType, + BasicJsonType, + CompatibleType, enable_if_t::value >> { static constexpr bool value = @@ -428,30 +494,38 @@ struct is_compatible_type_impl < template struct is_compatible_type - : is_compatible_type_impl {}; + : is_compatible_type_impl +{}; template -struct is_constructible_tuple : std::false_type {}; +struct is_constructible_tuple : std::false_type +{}; template -struct is_constructible_tuple> : conjunction...> {}; +struct is_constructible_tuple> : conjunction...> +{}; // a naive helper to check if a type is an ordered_map (exploits the fact that // ordered_map inherits capacity() from std::vector) -template +template struct is_ordered_map { using one = char; struct two { - char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) }; - template static one test( decltype(&C::capacity) ) ; - template static two test(...); + template + static one test(decltype(&C::capacity)); + template + static two test(...); - enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + enum + { + value = sizeof(test(nullptr)) == sizeof(char) + }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) }; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 9c27aabbe5..cbe9d8aa6a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -167,7 +167,7 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept // #include -#include // pair +#include // declval, pair // #include @@ -2214,6 +2214,83 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ +// #include + + +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + // This file contains all internal macro definitions // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them @@ -2504,6 +2581,41 @@ JSON_HEDLEY_DIAGNOSTIC_POP inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +// source: https://stackoverflow.com/a/26745591 + +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + #ifndef JSON_USE_IMPLICIT_CONVERSIONS #define JSON_USE_IMPLICIT_CONVERSIONS 1 #endif @@ -3202,10 +3314,13 @@ template struct identity_tag {}; // #include -#include // numeric_limits -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval -#include // tuple +#include // numeric_limits +#include // tuple +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + // #include @@ -3214,19 +3329,6 @@ template struct identity_tag {}; // #include - -namespace nlohmann -{ -namespace detail -{ -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; -} // namespace detail -} // namespace nlohmann - // #include @@ -3275,73 +3377,69 @@ struct iterator_traits::value>> } // namespace detail } // namespace nlohmann -// #include - -// #include +// #include -// #include +// #include -#include -// #include +// #include -// https://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann -{ -namespace detail -{ -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; +// source: https://stackoverflow.com/a/26745591 + +#define CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; -template class Op, class... Args> -struct detector>, Op, Args...> +namespace nlohmann { - using value_t = std::true_type; - using type = Op; -}; +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} -template class Op, class... Args> -using is_detected = typename detector::value_t; +// #include -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; -template class Op, class... Args> -using detected_t = typename detector::type; +// #include -template class Op, class... Args> -using detected_or = detector; -template class Op, class... Args> -using detected_or_t = typename detected_or::type; +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} -template class Op, class... Args> -using is_detected_exact = std::is_same>; +// #include -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; -} // namespace detail -} // namespace nlohmann +// #include // #include #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ @@ -3449,10 +3547,13 @@ namespace detail // In this case, T has to be properly CV-qualified to constraint the function arguments // (e.g. to_json(BasicJsonType&, const T&)) -template struct is_basic_json : std::false_type {}; +template +struct is_basic_json : std::false_type +{}; NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json : std::true_type {}; +struct is_basic_json : std::true_type +{}; ////////////////////// // json_ref helpers // @@ -3462,10 +3563,12 @@ template class json_ref; template -struct is_json_ref : std::false_type {}; +struct is_json_ref : std::false_type +{}; template -struct is_json_ref> : std::true_type {}; +struct is_json_ref> : std::true_type +{}; ////////////////////////// // aliases for detected // @@ -3492,9 +3595,6 @@ using reference_t = typename T::reference; template using iterator_category_t = typename T::iterator_category; -template -using iterator_t = typename T::iterator; - template using to_json_function = decltype(T::to_json(std::declval()...)); @@ -3506,13 +3606,14 @@ using get_template_function = decltype(std::declval().template get()); // trait checking if JSONSerializer::from_json(json const&, udt&) exists template -struct has_from_json : std::false_type {}; +struct has_from_json : std::false_type +{}; // trait checking if j.get is valid // use this trait instead of std::is_constructible or std::is_convertible, // both rely on, or make use of implicit conversions, and thus fail when T // has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) -template +template struct is_getable { static constexpr bool value = is_detected::value; @@ -3524,14 +3625,14 @@ struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = - is_detected_exact::value; + is_detected_exact::value; }; // This trait checks if JSONSerializer::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types template -struct has_non_default_from_json : std::false_type {}; +struct has_non_default_from_json : std::false_type +{}; template struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> @@ -3539,14 +3640,14 @@ struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_jso using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = - is_detected_exact::value; + is_detected_exact::value; }; // This trait checks if BasicJsonType::json_serializer::to_json exists // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. template -struct has_to_json : std::false_type {}; +struct has_to_json : std::false_type +{}; template struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> @@ -3554,66 +3655,80 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = - is_detected_exact::value; + is_detected_exact::value; }; - /////////////////// // is_ functions // /////////////////// // https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; +template +struct conjunction : std::true_type +{}; +template +struct conjunction : B1 +{}; template struct conjunction -: std::conditional, B1>::type {}; +: std::conditional, B1>::type + {}; // https://en.cppreference.com/w/cpp/types/negation -template struct negation : std::integral_constant < bool, !B::value > { }; +template +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. -template -struct is_default_constructible : std::is_default_constructible {}; +template +struct is_default_constructible : std::is_default_constructible +{}; -template +template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> +{}; -template +template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> +{}; -template +template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> +{}; -template +template struct is_default_constructible> - : conjunction...> {}; - - -template -struct is_constructible : std::is_constructible {}; + : conjunction...> +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible : std::is_constructible +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible> : is_default_constructible> +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible> : is_default_constructible> +{}; -template -struct is_constructible> : is_default_constructible> {}; +template +struct is_constructible> : is_default_constructible> +{}; +template +struct is_constructible> : is_default_constructible> +{}; template -struct is_iterator_traits : std::false_type {}; +struct is_iterator_traits : std::false_type +{}; template struct is_iterator_traits> @@ -3630,23 +3745,54 @@ struct is_iterator_traits> is_detected::value; }; +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + static constexpr auto has_begin = !std::is_same::value; + static constexpr auto has_end = !std::is_same::value; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = has_begin && has_end && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + // The following implementation of is_complete_type is taken from // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ // and is written by Xiang Fan who agreed to using it in this library. template -struct is_complete_type : std::false_type {}; +struct is_complete_type : std::false_type +{}; template -struct is_complete_type : std::true_type {}; +struct is_complete_type : std::true_type +{}; -template -struct is_compatible_object_type_impl : std::false_type {}; +template +struct is_compatible_object_type_impl : std::false_type +{}; template struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, + BasicJsonType, + CompatibleObjectType, enable_if_t < is_detected::value&& is_detected::value >> { @@ -3662,15 +3808,17 @@ struct is_compatible_object_type_impl < template struct is_compatible_object_type - : is_compatible_object_type_impl {}; + : is_compatible_object_type_impl +{}; -template -struct is_constructible_object_type_impl : std::false_type {}; +template +struct is_constructible_object_type_impl : std::false_type +{}; template struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, + BasicJsonType, + ConstructibleObjectType, enable_if_t < is_detected::value&& is_detected::value >> { @@ -3695,17 +3843,20 @@ struct is_constructible_object_type_impl < template struct is_constructible_object_type : is_constructible_object_type_impl {}; + ConstructibleObjectType> +{}; -template -struct is_compatible_string_type_impl : std::false_type {}; +template +struct is_compatible_string_type_impl : std::false_type +{}; template struct is_compatible_string_type_impl < - BasicJsonType, CompatibleStringType, - enable_if_t::value >> + BasicJsonType, + CompatibleStringType, + enable_if_t::value >> { static constexpr auto value = is_constructible::value; @@ -3713,17 +3864,20 @@ struct is_compatible_string_type_impl < template struct is_compatible_string_type - : is_compatible_string_type_impl {}; + : is_compatible_string_type_impl +{}; -template -struct is_constructible_string_type_impl : std::false_type {}; +template +struct is_constructible_string_type_impl : std::false_type +{}; template struct is_constructible_string_type_impl < - BasicJsonType, ConstructibleStringType, + BasicJsonType, + ConstructibleStringType, enable_if_t::value >> + value_type_t, + ConstructibleStringType>::value >> { static constexpr auto value = is_constructible struct is_constructible_string_type - : is_constructible_string_type_impl {}; + : is_constructible_string_type_impl +{}; template -struct is_compatible_array_type_impl : std::false_type {}; +struct is_compatible_array_type_impl : std::false_type +{}; template struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < is_detected::value&& + BasicJsonType, + CompatibleArrayType, + enable_if_t < is_detected::value&& -// This is needed because json_reverse_iterator has a ::iterator type... -// Therefore it is detected as a CompatibleArrayType. -// The real fix would be to have an Iterable concept. - !is_iterator_traits < - iterator_traits>::value >> + is_iterator_traits>>::value >> { static constexpr bool value = is_constructible::value; + range_value_t>::value; }; template struct is_compatible_array_type - : is_compatible_array_type_impl {}; + : is_compatible_array_type_impl +{}; template -struct is_constructible_array_type_impl : std::false_type {}; +struct is_constructible_array_type_impl : std::false_type +{}; template struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, + BasicJsonType, + ConstructibleArrayType, enable_if_t::value >> - : std::true_type {}; + : std::true_type +{}; template struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, + BasicJsonType, + ConstructibleArrayType, enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& is_default_constructible::value&& (std::is_move_assignable::value || std::is_copy_assignable::value)&& -is_detected::value&& is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& is_complete_type < -detected_t>::value >> +detected_t>::value >> { + using value_type = range_value_t; + static constexpr bool value = - // This is needed because json_reverse_iterator has a ::iterator type, - // furthermore, std::back_insert_iterator (and other iterators) have a - // base class `iterator`... Therefore it is detected as a - // ConstructibleArrayType. The real fix would be to have an Iterable - // concept. - !is_iterator_traits>::value && - - (std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, typename ConstructibleArrayType::value_type >::value); + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; }; template struct is_constructible_array_type - : is_constructible_array_type_impl {}; + : is_constructible_array_type_impl +{}; -template -struct is_compatible_integer_type_impl : std::false_type {}; +template +struct is_compatible_integer_type_impl : std::false_type +{}; template struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, + RealIntegerType, + CompatibleNumberIntegerType, enable_if_t < std::is_integral::value&& std::is_integral::value&& !std::is_same::value >> @@ -3825,14 +3983,17 @@ struct is_compatible_integer_type_impl < template struct is_compatible_integer_type : is_compatible_integer_type_impl {}; + CompatibleNumberIntegerType> +{}; template -struct is_compatible_type_impl: std::false_type {}; +struct is_compatible_type_impl : std::false_type +{}; template struct is_compatible_type_impl < - BasicJsonType, CompatibleType, + BasicJsonType, + CompatibleType, enable_if_t::value >> { static constexpr bool value = @@ -3841,30 +4002,38 @@ struct is_compatible_type_impl < template struct is_compatible_type - : is_compatible_type_impl {}; + : is_compatible_type_impl +{}; template -struct is_constructible_tuple : std::false_type {}; +struct is_constructible_tuple : std::false_type +{}; template -struct is_constructible_tuple> : conjunction...> {}; +struct is_constructible_tuple> : conjunction...> +{}; // a naive helper to check if a type is an ordered_map (exploits the fact that // ordered_map inherits capacity() from std::vector) -template +template struct is_ordered_map { using one = char; struct two { - char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) }; - template static one test( decltype(&C::capacity) ) ; - template static two test(...); + template + static one test(decltype(&C::capacity)); + template + static two test(...); - enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + enum + { + value = sizeof(test(nullptr)) == sizeof(char) + }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) }; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) @@ -26489,6 +26658,8 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT +#undef JSON_CATCH +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL // #include diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index 9666c55fec..ad159f31da 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -34,13 +34,13 @@ using nlohmann::json; #include #include +#include +#include #include #include +#include #include #include -#include -#include -#include // local variable is initialized but not referenced DOCTEST_MSVC_SUPPRESS_WARNING_PUSH @@ -73,10 +73,10 @@ TEST_CASE("README" * doctest::skip()) j["answer"]["everything"] = 42; // add an array that is stored as std::vector (using an initializer list) - j["list"] = { 1, 0, 2 }; + j["list"] = {1, 0, 2}; // add another object (using an initializer list of pairs) - j["object"] = { {"currency", "USD"}, {"value", 42.99} }; + j["object"] = {{"currency", "USD"}, {"value", 42.99}}; // instead, you could also write (which looks very similar to the JSON above) json j2 = @@ -85,18 +85,9 @@ TEST_CASE("README" * doctest::skip()) {"happy", true}, {"name", "Niels"}, {"nothing", nullptr}, - { - "answer", { - {"everything", 42} - } - }, + {"answer", {{"everything", 42}}}, {"list", {1, 0, 2}}, - { - "object", { - {"currency", "USD"}, - {"value", 42.99} - } - } + {"object", {{"currency", "USD"}, {"value", 42.99}}} }; } @@ -112,7 +103,7 @@ TEST_CASE("README" * doctest::skip()) CHECK(empty_object_explicit.is_object()); // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] - json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} }); + json array_not_object = json::array({{"currency", "USD"}, {"value", 42.99}}); CHECK(array_not_object.is_array()); CHECK(array_not_object.size() == 2); CHECK(array_not_object[0].is_array()); @@ -121,7 +112,7 @@ TEST_CASE("README" * doctest::skip()) { // create object from string literal - json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; // NOLINT(modernize-raw-string-literal) + json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; // NOLINT(modernize-raw-string-literal) // or even nicer with a raw string literal auto j2 = R"( @@ -135,7 +126,7 @@ TEST_CASE("README" * doctest::skip()) auto j3 = json::parse(R"({"happy": true, "pi": 3.141})"); // explicit conversion to string - std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} + std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} // serialization with pretty printing // pass in the amount of spaces to indent @@ -160,7 +151,7 @@ TEST_CASE("README" * doctest::skip()) CHECK(x == true); // iterate the array - for (json::iterator it = j.begin(); it != j.end(); ++it) // NOLINT(modernize-loop-convert) + for (json::iterator it = j.begin(); it != j.end(); ++it) // NOLINT(modernize-loop-convert) { std::cout << *it << '\n'; } @@ -178,10 +169,10 @@ TEST_CASE("README" * doctest::skip()) CHECK(foo == true); // other stuff - j.size(); // 3 entries - j.empty(); // false - j.type(); // json::value_t::array - j.clear(); // the array is empty again + j.size(); // 3 entries + j.empty(); // false + j.type(); // json::value_t::array + j.clear(); // the array is empty again // create an object json o; @@ -197,58 +188,58 @@ TEST_CASE("README" * doctest::skip()) } { - std::vector c_vector {1, 2, 3, 4}; + std::vector c_vector{1, 2, 3, 4}; json j_vec(c_vector); // [1, 2, 3, 4] - std::deque c_deque {1.2f, 2.3f, 3.4f, 5.6f}; + std::deque c_deque{1.2f, 2.3f, 3.4f, 5.6f}; json j_deque(c_deque); // [1.2, 2.3, 3.4, 5.6] - std::list c_list {true, true, false, true}; + std::list c_list{true, true, false, true}; json j_list(c_list); // [true, true, false, true] - std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; + std::forward_list c_flist{12345678909876, 23456789098765, 34567890987654, 45678909876543}; json j_flist(c_flist); // [12345678909876, 23456789098765, 34567890987654, 45678909876543] - std::array c_array {{1, 2, 3, 4}}; + std::array c_array{{1, 2, 3, 4}}; json j_array(c_array); // [1, 2, 3, 4] - std::set c_set {"one", "two", "three", "four", "one"}; - json j_set(c_set); // only one entry for "one" is used + std::set c_set{"one", "two", "three", "four", "one"}; + json j_set(c_set); // only one entry for "one" is used // ["four", "one", "three", "two"] - std::unordered_set c_uset {"one", "two", "three", "four", "one"}; - json j_uset(c_uset); // only one entry for "one" is used + std::unordered_set c_uset{"one", "two", "three", "four", "one"}; + json j_uset(c_uset); // only one entry for "one" is used // maybe ["two", "three", "four", "one"] - std::multiset c_mset {"one", "two", "one", "four"}; - json j_mset(c_mset); // both entries for "one" are used + std::multiset c_mset{"one", "two", "one", "four"}; + json j_mset(c_mset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] - std::unordered_multiset c_umset {"one", "two", "one", "four"}; - json j_umset(c_umset); // both entries for "one" are used + std::unordered_multiset c_umset{"one", "two", "one", "four"}; + json j_umset(c_umset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] } { - std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; + std::map c_map{{"one", 1}, {"two", 2}, {"three", 3}}; json j_map(c_map); // {"one": 1, "two": 2, "three": 3} - std::unordered_map c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} }; + std::unordered_map c_umap{{"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f}}; json j_umap(c_umap); // {"one": 1.2, "two": 2.3, "three": 3.4} - std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; - json j_mmap(c_mmap); // only one entry for key "three" is used + std::multimap c_mmap{{"one", true}, {"two", true}, {"three", false}, {"three", true}}; + json j_mmap(c_mmap); // only one entry for key "three" is used // maybe {"one": true, "two": true, "three": true} - std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; - json j_ummap(c_ummap); // only one entry for key "three" is used + std::unordered_multimap c_ummap{{"one", true}, {"two", true}, {"three", false}, {"three", true}}; + json j_ummap(c_ummap); // only one entry for key "three" is used // maybe {"one": true, "two": true, "three": true} } diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index f83ef9a0ac..7c2a1c636e 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -39,7 +39,6 @@ using nlohmann::json; #include #include #include -#include #include namespace udt @@ -54,19 +53,25 @@ enum class country struct age { int m_val; - age(int rhs = 0) : m_val(rhs) {} + age(int rhs = 0) + : m_val(rhs) + {} }; struct name { std::string m_val; - name(std::string rhs = "") : m_val(std::move(rhs)) {} + name(std::string rhs = "") + : m_val(std::move(rhs)) + {} }; struct address { std::string m_val; - address(std::string rhs = "") : m_val(std::move(rhs)) {} + address(std::string rhs = "") + : m_val(std::move(rhs)) + {} }; struct person @@ -75,7 +80,11 @@ struct person name m_name{}; country m_country{}; person() = default; - person(const age& a, name n, const country& c) : m_age(a), m_name(std::move(n)), m_country(c) {} + person(const age& a, name n, const country& c) + : m_age(a) + , m_name(std::move(n)) + , m_country(c) + {} }; struct contact @@ -83,7 +92,10 @@ struct contact person m_person{}; address m_address{}; contact() = default; - contact(person p, address a) : m_person(std::move(p)), m_address(std::move(a)) {} + contact(person p, address a) + : m_person(std::move(p)) + , m_address(std::move(a)) + {} }; struct contact_book @@ -91,27 +103,30 @@ struct contact_book name m_book_name{}; std::vector m_contacts{}; contact_book() = default; - contact_book(name n, std::vector c) : m_book_name(std::move(n)), m_contacts(std::move(c)) {} + contact_book(name n, std::vector c) + : m_book_name(std::move(n)) + , m_contacts(std::move(c)) + {} }; -} // namespace udt +} // namespace udt // to_json methods namespace udt { // templates because of the custom_json tests (see below) -template +template static void to_json(BasicJsonType& j, age a) { j = a.m_val; } -template +template static void to_json(BasicJsonType& j, const name& n) { j = n.m_val; } -template +template static void to_json(BasicJsonType& j, country c) { switch (c) @@ -130,7 +145,7 @@ static void to_json(BasicJsonType& j, country c) } } -template +template static void to_json(BasicJsonType& j, const person& p) { j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}}; @@ -183,24 +198,24 @@ static bool operator==(const contact_book& lhs, const contact_book& rhs) return std::tie(lhs.m_book_name, lhs.m_contacts) == std::tie(rhs.m_book_name, rhs.m_contacts); } -} // namespace udt +} // namespace udt // from_json methods namespace udt { -template +template static void from_json(const BasicJsonType& j, age& a) { a.m_val = j.template get(); } -template +template static void from_json(const BasicJsonType& j, name& n) { n.m_val = j.template get(); } -template +template static void from_json(const BasicJsonType& j, country& c) { const auto str = j.template get(); @@ -216,7 +231,7 @@ static void from_json(const BasicJsonType& j, country& c) c = it->second; } -template +template static void from_json(const BasicJsonType& j, person& p) { p.m_age = j["age"].template get(); @@ -240,16 +255,14 @@ static void from_json(const nlohmann::json& j, contact_book& cb) cb.m_book_name = j["name"].get(); cb.m_contacts = j["contacts"].get>(); } -} // namespace udt +} // namespace udt TEST_CASE("basic usage" * doctest::test_suite("udt")) { - // a bit narcissistic maybe :) ? const udt::age a { - 23 - }; + 23}; const udt::name n{"theo"}; const udt::country c{udt::country::france}; const udt::person sfinae_addict{a, n, c}; @@ -271,7 +284,6 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) CHECK( json(book) == R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); - } SECTION("conversion from json via free-functions") @@ -351,13 +363,15 @@ struct legacy_type { std::string number{}; legacy_type() = default; - legacy_type(std::string n) : number(std::move(n)) {} + legacy_type(std::string n) + : number(std::move(n)) + {} }; -} // namespace udt +} // namespace udt namespace nlohmann { -template +template struct adl_serializer> { static void to_json(json& j, const std::shared_ptr& opt) @@ -380,12 +394,12 @@ struct adl_serializer> } else { - opt.reset(new T(j.get())); // NOLINT(cppcoreguidelines-owning-memory) + opt.reset(new T(j.get())); // NOLINT(cppcoreguidelines-owning-memory) } } }; -template <> +template<> struct adl_serializer { static void to_json(json& j, const udt::legacy_type& l) @@ -398,7 +412,7 @@ struct adl_serializer l.number = std::to_string(j.get()); } }; -} // namespace nlohmann +} // namespace nlohmann TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt")) { @@ -411,7 +425,7 @@ TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt")) json j = optPerson; CHECK(j.is_null()); - optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-shared) + optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-shared) j = optPerson; CHECK_FALSE(j.is_null()); @@ -454,7 +468,7 @@ TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt")) namespace nlohmann { -template <> +template<> struct adl_serializer> { using type = std::vector; @@ -474,7 +488,7 @@ struct adl_serializer> return {4.0, 5.0, 6.0}; } }; -} // namespace nlohmann +} // namespace nlohmann TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt")) { @@ -487,7 +501,7 @@ TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt") namespace nlohmann { -template +template struct adl_serializer> { static void to_json(json& j, const std::unique_ptr& opt) @@ -513,7 +527,7 @@ struct adl_serializer> return std::unique_ptr(new T(j.get())); } }; -} // namespace nlohmann +} // namespace nlohmann TEST_CASE("Non-copyable types" * doctest::test_suite("udt")) { @@ -524,7 +538,7 @@ TEST_CASE("Non-copyable types" * doctest::test_suite("udt")) json j = optPerson; CHECK(j.is_null()); - optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-unique) + optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-unique) j = optPerson; CHECK_FALSE(j.is_null()); @@ -549,14 +563,16 @@ TEST_CASE("Non-copyable types" * doctest::test_suite("udt")) // custom serializer - advanced usage // pack structs that are pod-types (but not scalar types) // relies on adl for any other type -template +template struct pod_serializer { // use adl for non-pods, or scalar types template < - typename BasicJsonType, typename U = T, + typename BasicJsonType, + typename U = T, typename std::enable_if < - !(std::is_pod::value && std::is_class::value), int >::type = 0 > + !(std::is_pod::value && std::is_class::value), + int >::type = 0 > static void from_json(const BasicJsonType& j, U& t) { using nlohmann::from_json; @@ -564,10 +580,8 @@ struct pod_serializer } // special behaviour for pods - template < typename BasicJsonType, typename U = T, - typename std::enable_if < - std::is_pod::value && std::is_class::value, int >::type = 0 > - static void from_json(const BasicJsonType& j, U& t) + template < typename BasicJsonType, typename U = T, typename std::enable_if < std::is_pod::value && std::is_class::value, int >::type = 0 > + static void from_json(const BasicJsonType& j, U& t) { std::uint64_t value = 0; // The following block is no longer relevant in this serializer, make another one that shows the issue @@ -590,21 +604,21 @@ struct pod_serializer } template < - typename BasicJsonType, typename U = T, + typename BasicJsonType, + typename U = T, typename std::enable_if < - !(std::is_pod::value && std::is_class::value), int >::type = 0 > - static void to_json(BasicJsonType& j, const T& t) + !(std::is_pod::value && std::is_class::value), + int >::type = 0 > + static void to_json(BasicJsonType& j, const T& t) { using nlohmann::to_json; to_json(j, t); } - template < typename BasicJsonType, typename U = T, - typename std::enable_if < - std::is_pod::value && std::is_class::value, int >::type = 0 > - static void to_json(BasicJsonType& j, const T& t) noexcept + template < typename BasicJsonType, typename U = T, typename std::enable_if < std::is_pod::value && std::is_class::value, int >::type = 0 > + static void to_json(BasicJsonType& j, const T& t) noexcept { - const auto* bytes = static_cast< const unsigned char*>(static_cast(&t)); + const auto* bytes = static_cast(static_cast(&t)); std::uint64_t value = 0; std::memcpy(&value, bytes, sizeof(value)); nlohmann::to_json(j, value); @@ -624,16 +638,18 @@ struct non_pod { std::string s{}; non_pod() = default; - non_pod(std::string S) : s(std::move(S)) {} + non_pod(std::string S) + : s(std::move(S)) + {} }; -template +template static void to_json(BasicJsonType& j, const non_pod& np) { j = np.s; } -template +template static void from_json(const BasicJsonType& j, non_pod& np) { np.s = j.template get(); @@ -645,7 +661,7 @@ static bool operator==(small_pod lhs, small_pod rhs) noexcept std::tie(rhs.begin, rhs.middle, rhs.end); } -static bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept +static bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept { return lhs.s == rhs.s; } @@ -654,13 +670,12 @@ static std::ostream& operator<<(std::ostream& os, small_pod l) { return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end; } -} // namespace udt +} // namespace udt TEST_CASE("custom serializer for pods" * doctest::test_suite("udt")) { using custom_json = - nlohmann::basic_json; + nlohmann::basic_json; auto p = udt::small_pod{42, '/', 42}; custom_json j = p; @@ -675,12 +690,12 @@ TEST_CASE("custom serializer for pods" * doctest::test_suite("udt")) CHECK(np == np2); } -template +template struct another_adl_serializer; using custom_json = nlohmann::basic_json; -template +template struct another_adl_serializer { static void from_json(const custom_json& j, T& t) @@ -801,12 +816,14 @@ struct incomplete; // std::is_constructible is broken on macOS' libc++ // use the cppreference implementation -template -struct is_constructible_patched : std::false_type {}; +template +struct is_constructible_patched : std::false_type +{}; -template -struct is_constructible_patched())))> : std::true_type {}; -} // namespace +template +struct is_constructible_patched())))> : std::true_type +{}; +} // namespace TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context" * doctest::test_suite("udt")) { @@ -819,17 +836,18 @@ class Evil { public: Evil() = default; - template - Evil(T t) : m_i(sizeof(t)) + template + Evil(T t) + : m_i(sizeof(t)) { - static_cast(t); // fix MSVC's C4100 warning + static_cast(t); // fix MSVC's C4100 warning } int m_i = 0; }; void from_json(const json& /*unused*/, Evil& /*unused*/) {} -} // namespace +} // namespace TEST_CASE("Issue #924") { @@ -846,8 +864,39 @@ TEST_CASE("Issue #924") TEST_CASE("Issue #1237") { - struct non_convertible_type {}; + struct non_convertible_type + {}; static_assert(!std::is_convertible::value, ""); } +namespace +{ +class no_iterator_type +{ + public: + no_iterator_type(std::initializer_list l) + : _v(l) + {} + + std::vector::const_iterator begin() const + { + return _v.begin(); + } + + std::vector::const_iterator end() const + { + return _v.end(); + } + + private: + std::vector _v; +}; +} // namespace + +TEST_CASE("compatible array type, without iterator type alias") +{ + no_iterator_type vec{1, 2, 3}; + json j = vec; +} + DOCTEST_GCC_SUPPRESS_WARNING_POP