Skip to content

Commit

Permalink
Dedup ADL begin/end lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Apr 22, 2024
1 parent a3e0931 commit a382fda
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 45 deletions.
48 changes: 10 additions & 38 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,31 +61,19 @@ auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}

template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};

template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};

// Member function overload
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
auto begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
auto end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());

// ADL overload. Only participates in overload resolution if member functions
// are not found.
// Overloads that do ADL if member functions not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
auto range_begin(T&& rng) -> decltype(begin(static_cast<T&&>(rng))) {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
auto range_end(T&& rng) -> decltype(end(static_cast<T&&>(rng))) {
return end(static_cast<T&&>(rng));
}

Expand Down Expand Up @@ -619,22 +607,6 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {begin, end, sep};
}

namespace detail {
// ADL helpers for fmt::join()
namespace adl {
using std::begin;
using std::end;

template <typename Range> auto adlbegin(Range& r) -> decltype(begin(r)) {
return begin(r);
}

template <typename Range> auto adlend(Range& r) -> decltype(end(r)) {
return end(r);
}
} // namespace adl
} // namespace detail

/**
\rst
Returns a view that formats `range` with elements separated by `sep`.
Expand All @@ -652,10 +624,10 @@ template <typename Range> auto adlend(Range& r) -> decltype(end(r)) {
\endrst
*/
template <typename Range>
auto join(Range&& range, string_view sep)
-> join_view<decltype(detail::adl::adlbegin(range)),
decltype(detail::adl::adlend(range))> {
return join(detail::adl::adlbegin(range), detail::adl::adlend(range), sep);
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}

template <typename Char, typename... T> struct tuple_join_view : detail::view {
Expand Down
12 changes: 5 additions & 7 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -462,18 +462,16 @@ TEST(ranges_test, join_range) {
}

namespace adl {
struct vec : std::vector<int> {
using std::vector<int>::vector; // inherit all constructors
struct vec {
int n[2] = {42, 43};
};

// ADL-found begin() and end() skip the first and last element
auto begin(vec& v) -> typename vec::iterator { return v.begin() + 1; }
auto end(vec& v) -> typename vec::iterator { return v.end() - 1; }
auto begin(const vec& v) -> const int* { return v.n; }
auto end(const vec& v) -> const int* { return v.n + 2; }
}

TEST(ranges_test, format_join_adl_begin_end) {
auto v = adl::vec{41, 42, 43, 44};
EXPECT_EQ(fmt::format("{}", fmt::join(v, "/")), "42/43");
EXPECT_EQ(fmt::format("{}", fmt::join(adl::vec(), "/")), "42/43");
}

#endif // FMT_RANGES_TEST_ENABLE_JOIN
Expand Down

0 comments on commit a382fda

Please sign in to comment.