diff --git a/src/json.hpp b/src/json.hpp index 9d48e7a65..b3ed18180 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -97,39 +97,354 @@ SOFTWARE. */ namespace nlohmann { +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; +template +using remove_cv_t = typename std::remove_cv::type; +template +using remove_reference_t = typename std::remove_reference::type; + +template +using uncvref_t = remove_cv_t>; + +template +using conditional_t = typename std::conditional::type; + +// Taken from http://stackoverflow.com/questions/26936640/how-to-implement-is-enum-class-type-trait +template +using is_scoped_enum = + std::integral_constant::value and + std::is_enum::value>; + +template +using is_unscoped_enum = + std::integral_constant::value and + std::is_enum::value>; + +// TODO update this doc /*! @brief unnamed namespace with internal helper functions @since version 1.0.0 */ -namespace + +namespace detail { +// Very useful construct against boilerplate (more boilerplate needed than in C++17: http://en.cppreference.com/w/cpp/types/void_t) +template struct make_void { using type = void; }; +template using void_t = typename make_void::type; + +// Implementation of 3 C++17 constructs: conjunction, disjunction, negation. +// This is needed to avoid evaluating all the traits in a condition +// +// For example: not std::is_same::value and has_value_type::value +// will not compile when T = void (on MSVC at least) +// Whereas conjunction>, has_value_type>::value +// will stop evaluating if negation<...>::value == false +// +// Please note that those constructs must be used with caution, since symbols can +// become very long quickly (which can slow down compilation and cause MSVC internal compiler errors) +// Only use it when you have too (see example ahead) +template struct conjunction : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction + : conditional_t, B1> {}; + +template struct negation : std::integral_constant {}; +template struct disjunction : std::false_type {}; +template struct disjunction : B1 {}; +template +struct disjunction + : conditional_t> {}; + /*! @brief Helper to determine whether there's a key_type for T. - Thus helper is used to tell associative containers apart from other containers such as sequence containers. For instance, `std::map` passes the test as it contains a `mapped_type`, whereas `std::vector` fails the test. - @sa http://stackoverflow.com/a/7728728/266378 @since version 1.0.0, overworked in version 2.0.6 */ -template -struct has_mapped_type -{ - private: - template - static int detect(U&&); +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template struct has_##type { \ + private: \ + template \ + static int detect(U &&); \ + \ + static void detect(...); \ + \ + public: \ + static constexpr bool value = \ + std::is_integral()))>::value; \ + }; - static void detect(...); - public: - static constexpr bool value = - std::is_integral()))>::value; +NLOHMANN_JSON_HAS_HELPER(mapped_type) +NLOHMANN_JSON_HAS_HELPER(key_type) +NLOHMANN_JSON_HAS_HELPER(value_type) +NLOHMANN_JSON_HAS_HELPER(iterator) + +#undef NLOHMANN_JSON_HAS_HELPER + +template +struct is_compatible_object_type_impl : std::false_type{}; + +template +struct is_compatible_object_type_impl +{ + static constexpr auto value = + std::is_constructible::value and + std::is_constructible::value; +}; + +template +struct is_compatible_object_type +{ + // As noted ahead, we need to stop evaluating traits if CompatibleObjectType = void + // hence the conjunction + static auto constexpr value = is_compatible_object_type_impl< + conjunction>, + has_mapped_type, + has_key_type>::value, + RealType, CompatibleObjectType>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type{}; + +template +struct is_compatible_array_type_impl +{ + static constexpr auto value = + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value; +}; + +template +struct is_compatible_array_type +{ + // the check for CompatibleArrayType = void is done in is_compatible_object_type + // but we need the conjunction here as well + static auto constexpr value = is_compatible_array_type_impl< + conjunction>, + has_value_type, + has_iterator>::value, + BasicJson, CompatibleArrayType>::value; +}; + +template +struct is_compatible_integer_type_impl : std::false_type{}; + +template +struct is_compatible_integer_type_impl +{ + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + std::is_constructible::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type +{ + static constexpr auto value = is_compatible_integer_type_impl< + std::is_arithmetic::value, RealIntegerType, + CompatibleNumberIntegerType>::value; +}; + +template +struct is_compatible_float_type +{ + static constexpr auto value = + std::is_constructible::value and + std::is_floating_point::value; +}; + +template +struct is_compatible_basic_json_type +{ + static auto constexpr value = + is_unscoped_enum::value or + std::is_same::value or + std::is_constructible::value or + std::is_same::value or + is_compatible_array_type::value or + is_compatible_object_type::value or + is_compatible_float_type::value or + is_compatible_integer_type::value or + is_compatible_integer_type::value; +}; + +template +struct is_basic_json_nested_class +{ + static auto constexpr value = std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&, udt&) exists +template