diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 76a0dc62c..c2a65bdda 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,45 @@ 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__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#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; \ + }; \ + } /* namespace detail2 */ \ + \ + 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 3f56e4b85..67bf1466b 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -19,5 +19,6 @@ #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT +#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 000000000..da937144c --- /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); +} // namespace nlohmann 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 000000000..190900739 --- /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); +} // namespace nlohmann diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index ede55acc2..ca6051e7c 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -5,8 +5,11 @@ #include // declval #include // tuple -#include #include + +#include +#include +#include #include #include #include @@ -79,9 +82,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()...)); @@ -217,6 +217,31 @@ 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; + + // 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 = !std::is_same::value && !std::is_same::value && 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. @@ -291,8 +316,9 @@ struct is_compatible_string_type_impl : std::false_type {}; template struct is_compatible_string_type_impl < BasicJsonType, CompatibleStringType, - enable_if_t::value >> + enable_if_t::value >> { static constexpr auto value = is_constructible::value; @@ -327,17 +353,13 @@ struct is_compatible_array_type_impl : std::false_type {}; template struct is_compatible_array_type_impl < BasicJsonType, CompatibleArrayType, - enable_if_t < is_detected::value&& + 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 @@ -359,28 +381,26 @@ struct is_constructible_array_type_impl < 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 >> { - 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 && + using value_type = range_value_t; - (std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, typename ConstructibleArrayType::value_type >::value); + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; }; template diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 5899a7945..2832fca50 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,45 @@ 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__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#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; \ + }; \ + } /* namespace detail2 */ \ + \ + 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 @@ -3207,6 +3323,9 @@ template struct identity_tag {}; #include // declval #include // tuple +// #include + + // #include @@ -3214,19 +3333,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,74 +3381,32 @@ struct iterator_traits::value>> } // namespace detail } // namespace nlohmann +// #include + + // #include + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} // namespace nlohmann + // #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; -}; - -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 - // #include #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ @@ -3492,9 +3556,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()...)); @@ -3630,6 +3691,31 @@ 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; + + // 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 = !std::is_same::value && !std::is_same::value && 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. @@ -3704,8 +3790,9 @@ struct is_compatible_string_type_impl : std::false_type {}; template struct is_compatible_string_type_impl < BasicJsonType, CompatibleStringType, - enable_if_t::value >> + enable_if_t::value >> { static constexpr auto value = is_constructible::value; @@ -3740,17 +3827,13 @@ struct is_compatible_array_type_impl : std::false_type {}; template struct is_compatible_array_type_impl < BasicJsonType, CompatibleArrayType, - enable_if_t < is_detected::value&& + 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 @@ -3772,28 +3855,26 @@ struct is_constructible_array_type_impl < 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 >> { - 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 && + using value_type = range_value_t; - (std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, typename ConstructibleArrayType::value_type >::value); + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; }; template @@ -26499,6 +26580,7 @@ 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 NLOHMANN_CAN_CALL_STD_FUNC_IMPL // #include diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index f83ef9a0a..eed6c4132 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -850,4 +850,34 @@ TEST_CASE("Issue #1237") 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