diff --git a/include/fmt/core.h b/include/fmt/core.h index 9bfee019..d7ca348e 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -129,6 +129,24 @@ # define FMT_CONSTEXPR_CHAR_TRAITS #endif +// MSVC goofs up the range checking under a specific version +#ifndef FMT_RANGE_CHECKS +# if (!FMT_MSC_VERSION || FMT_MSC_VERSION > 1800) +# define FMT_RANGE_CHECKS 1 +# else +# define FMT_RANGE_CHECKS 0 +# endif +#endif + +// Only on if we know we can do the begin/end range checking properly +#ifndef FMT_OUTPUT_RANGES +# if FMT_RANGE_CHECKS +# define FMT_OUTPUT_RANGES 1 +# else +# define FMT_OUTPUT_RANGES 0 +# endif +#endif + // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ @@ -1528,6 +1546,82 @@ template using void_t = typename void_t_impl::type; template using void_t = void; #endif +template struct is_range_ : std::false_type {}; + +#if FMT_RANGE_CHECKS + +# define FMT_DECLTYPE_RETURN(val) \ + ->decltype(val) { return val; } \ + static_assert( \ + true, "") // This makes it so that a semicolon is required after the + // macro, which helps clang-format handle the formatting. + +// C array overload +template +auto range_begin(const T (&arr)[N]) -> const T* { + return arr; +} +template +auto range_end(const T (&arr)[N]) -> const T* { + return arr + N; +} + +template +struct has_member_fn_begin_end_t : std::false_type {}; + +template +struct has_member_fn_begin_end_t().begin()), + decltype(std::declval().end())>> + : std::true_type {}; + +// Member function overload +template +auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).begin()); +template +auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).end()); + +// ADL overload. Only participates in overload resolution if member functions +// are not found. +template +auto range_begin(T&& rng) + -> enable_if_t::value, + decltype(begin(static_cast(rng)))> { + return begin(static_cast(rng)); +} +template +auto range_end(T&& rng) -> enable_if_t::value, + decltype(end(static_cast(rng)))> { + return end(static_cast(rng)); +} + +template +struct has_const_begin_end : std::false_type {}; +template +struct has_mutable_begin_end : std::false_type {}; + +template +struct has_const_begin_end< + T, + void_t< + decltype(detail::range_begin(std::declval&>())), + decltype(detail::range_end(std::declval&>()))>> + : std::true_type {}; + +template +struct has_mutable_begin_end< + T, void_t())), + decltype(detail::range_end(std::declval())), + // the extra int here is because older versions of MSVC don't + // SFINAE properly unless there are distinct types + int>> : std::true_type {}; + +template +struct is_range_ + : std::integral_constant::value || + has_mutable_begin_end::value)> {}; +# undef FMT_DECLTYPE_RETURN +#endif + template struct is_output_iterator : std::false_type {}; @@ -1613,6 +1707,17 @@ template FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { return make_arg(val); } + +template ::value> +struct out_storage { + Out out_; +}; + +template +struct out_storage { + decltype(range_begin(std::declval())) out_; + decltype(range_end(std::declval())) out_last_; +}; } // namespace detail FMT_BEGIN_EXPORT diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index c0b51aee..2e6ef6d3 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -86,82 +86,6 @@ template class is_set { template struct conditional_helper {}; -template struct is_range_ : std::false_type {}; - -#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 - -# define FMT_DECLTYPE_RETURN(val) \ - ->decltype(val) { return val; } \ - static_assert( \ - true, "") // This makes it so that a semicolon is required after the - // macro, which helps clang-format handle the formatting. - -// C array overload -template -auto range_begin(const T (&arr)[N]) -> const T* { - return arr; -} -template -auto range_end(const T (&arr)[N]) -> const T* { - return arr + N; -} - -template -struct has_member_fn_begin_end_t : std::false_type {}; - -template -struct has_member_fn_begin_end_t().begin()), - decltype(std::declval().end())>> - : std::true_type {}; - -// Member function overload -template -auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).begin()); -template -auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).end()); - -// ADL overload. Only participates in overload resolution if member functions -// are not found. -template -auto range_begin(T&& rng) - -> enable_if_t::value, - decltype(begin(static_cast(rng)))> { - return begin(static_cast(rng)); -} -template -auto range_end(T&& rng) -> enable_if_t::value, - decltype(end(static_cast(rng)))> { - return end(static_cast(rng)); -} - -template -struct has_const_begin_end : std::false_type {}; -template -struct has_mutable_begin_end : std::false_type {}; - -template -struct has_const_begin_end< - T, - void_t< - decltype(detail::range_begin(std::declval&>())), - decltype(detail::range_end(std::declval&>()))>> - : std::true_type {}; - -template -struct has_mutable_begin_end< - T, void_t())), - decltype(detail::range_end(std::declval())), - // the extra int here is because older versions of MSVC don't - // SFINAE properly unless there are distinct types - int>> : std::true_type {}; - -template -struct is_range_ - : std::integral_constant::value || - has_mutable_begin_end::value)> {}; -# undef FMT_DECLTYPE_RETURN -#endif - // tuple_size and tuple_element check. template class is_tuple_like_ { template diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 56fbd382..e2c4a1c8 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -5,6 +5,11 @@ // // For the license information refer to format.h. +// Disable bogus MSVC warnings. +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS +#endif + #include "fmt/chrono.h" #include @@ -1027,4 +1032,4 @@ TEST(chrono_test, glibc_extensions) { TEST(chrono_test, out_of_range) { auto d = std::chrono::duration(538976288); EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error); -} \ No newline at end of file +} diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 90ada586..1b62e6bf 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -5,6 +5,11 @@ // // For the license information refer to format.h. +// Disable bogus MSVC warnings. +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS +#endif + #include "fmt/xchar.h" #include