Add ADL support to range based <algorithm> extensions

This adds support for ADL in the range based <algorithm> extensions
(llvm::for_each etc.).

Also adds the helper functions llvm::adl::begin and llvm::adl::end which wrap
std::begin and std::end with ADL support.

Saw this was missing from a recent llvm weekly post about adding llvm::for_each
and thought I might add it.

Patch by Stephen Dollberg!

Differential Revision: https://reviews.llvm.org/D40006

llvm-svn: 318703
This commit is contained in:
David Blaikie 2017-11-20 22:12:55 +00:00
parent 0282966e38
commit 2bc260aba2
2 changed files with 124 additions and 41 deletions

View File

@ -130,6 +130,52 @@ inline void deleter(T *Ptr) {
// Extra additions to <iterator>
//===----------------------------------------------------------------------===//
namespace adl_detail {
using std::begin;
template <typename ContainerTy>
auto adl_begin(ContainerTy &&container)
-> decltype(begin(std::forward<ContainerTy>(container))) {
return begin(std::forward<ContainerTy>(container));
}
using std::end;
template <typename ContainerTy>
auto adl_end(ContainerTy &&container)
-> decltype(end(std::forward<ContainerTy>(container))) {
return end(std::forward<ContainerTy>(container));
}
using std::swap;
template <typename T>
void adl_swap(T &&lhs, T &&rhs) noexcept(noexcept(swap(std::declval<T>(),
std::declval<T>()))) {
swap(std::forward<T>(lhs), std::forward<T>(rhs));
}
} // end namespace adl_detail
template <typename ContainerTy>
auto adl_begin(ContainerTy &&container)
-> decltype(adl_detail::adl_begin(std::forward<ContainerTy>(container))) {
return adl_detail::adl_begin(std::forward<ContainerTy>(container));
}
template <typename ContainerTy>
auto adl_end(ContainerTy &&container)
-> decltype(adl_detail::adl_end(std::forward<ContainerTy>(container))) {
return adl_detail::adl_end(std::forward<ContainerTy>(container));
}
template <typename T>
void adl_swap(T &&lhs, T &&rhs) noexcept(
noexcept(adl_detail::adl_swap(std::declval<T>(), std::declval<T>()))) {
adl_detail::adl_swap(std::forward<T>(lhs), std::forward<T>(rhs));
}
// mapped_iterator - This is a simple iterator adapter that causes a function to
// be applied whenever operator* is invoked on the iterator.
@ -758,106 +804,105 @@ void DeleteContainerSeconds(Container &C) {
/// pass begin/end explicitly.
template <typename R, typename UnaryPredicate>
UnaryPredicate for_each(R &&Range, UnaryPredicate P) {
return std::for_each(std::begin(Range), std::end(Range), P);
return std::for_each(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::all_of which take ranges instead of having to pass
/// begin/end explicitly.
template <typename R, typename UnaryPredicate>
bool all_of(R &&Range, UnaryPredicate P) {
return std::all_of(std::begin(Range), std::end(Range), P);
return std::all_of(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::any_of which take ranges instead of having to pass
/// begin/end explicitly.
template <typename R, typename UnaryPredicate>
bool any_of(R &&Range, UnaryPredicate P) {
return std::any_of(std::begin(Range), std::end(Range), P);
return std::any_of(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::none_of which take ranges instead of having to pass
/// begin/end explicitly.
template <typename R, typename UnaryPredicate>
bool none_of(R &&Range, UnaryPredicate P) {
return std::none_of(std::begin(Range), std::end(Range), P);
return std::none_of(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::find which take ranges instead of having to pass
/// begin/end explicitly.
template <typename R, typename T>
auto find(R &&Range, const T &Val) -> decltype(std::begin(Range)) {
return std::find(std::begin(Range), std::end(Range), Val);
auto find(R &&Range, const T &Val) -> decltype(adl_begin(Range)) {
return std::find(adl_begin(Range), adl_end(Range), Val);
}
/// Provide wrappers to std::find_if which take ranges instead of having to pass
/// begin/end explicitly.
template <typename R, typename UnaryPredicate>
auto find_if(R &&Range, UnaryPredicate P) -> decltype(std::begin(Range)) {
return std::find_if(std::begin(Range), std::end(Range), P);
auto find_if(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
return std::find_if(adl_begin(Range), adl_end(Range), P);
}
template <typename R, typename UnaryPredicate>
auto find_if_not(R &&Range, UnaryPredicate P) -> decltype(std::begin(Range)) {
return std::find_if_not(std::begin(Range), std::end(Range), P);
auto find_if_not(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
return std::find_if_not(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::remove_if which take ranges instead of having to
/// pass begin/end explicitly.
template <typename R, typename UnaryPredicate>
auto remove_if(R &&Range, UnaryPredicate P) -> decltype(std::begin(Range)) {
return std::remove_if(std::begin(Range), std::end(Range), P);
auto remove_if(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
return std::remove_if(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::copy_if which take ranges instead of having to
/// pass begin/end explicitly.
template <typename R, typename OutputIt, typename UnaryPredicate>
OutputIt copy_if(R &&Range, OutputIt Out, UnaryPredicate P) {
return std::copy_if(std::begin(Range), std::end(Range), Out, P);
return std::copy_if(adl_begin(Range), adl_end(Range), Out, P);
}
/// Wrapper function around std::find to detect if an element exists
/// in a container.
template <typename R, typename E>
bool is_contained(R &&Range, const E &Element) {
return std::find(std::begin(Range), std::end(Range), Element) !=
std::end(Range);
return std::find(adl_begin(Range), adl_end(Range), Element) != adl_end(Range);
}
/// Wrapper function around std::count to count the number of times an element
/// \p Element occurs in the given range \p Range.
template <typename R, typename E>
auto count(R &&Range, const E &Element) -> typename std::iterator_traits<
decltype(std::begin(Range))>::difference_type {
return std::count(std::begin(Range), std::end(Range), Element);
auto count(R &&Range, const E &Element) ->
typename std::iterator_traits<decltype(adl_begin(Range))>::difference_type {
return std::count(adl_begin(Range), adl_end(Range), Element);
}
/// Wrapper function around std::count_if to count the number of times an
/// element satisfying a given predicate occurs in a range.
template <typename R, typename UnaryPredicate>
auto count_if(R &&Range, UnaryPredicate P) -> typename std::iterator_traits<
decltype(std::begin(Range))>::difference_type {
return std::count_if(std::begin(Range), std::end(Range), P);
auto count_if(R &&Range, UnaryPredicate P) ->
typename std::iterator_traits<decltype(adl_begin(Range))>::difference_type {
return std::count_if(adl_begin(Range), adl_end(Range), P);
}
/// Wrapper function around std::transform to apply a function to a range and
/// store the result elsewhere.
template <typename R, typename OutputIt, typename UnaryPredicate>
OutputIt transform(R &&Range, OutputIt d_first, UnaryPredicate P) {
return std::transform(std::begin(Range), std::end(Range), d_first, P);
return std::transform(adl_begin(Range), adl_end(Range), d_first, P);
}
/// Provide wrappers to std::partition which take ranges instead of having to
/// pass begin/end explicitly.
template <typename R, typename UnaryPredicate>
auto partition(R &&Range, UnaryPredicate P) -> decltype(std::begin(Range)) {
return std::partition(std::begin(Range), std::end(Range), P);
auto partition(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
return std::partition(adl_begin(Range), adl_end(Range), P);
}
/// Provide wrappers to std::lower_bound which take ranges instead of having to
/// pass begin/end explicitly.
template <typename R, typename ForwardIt>
auto lower_bound(R &&Range, ForwardIt I) -> decltype(std::begin(Range)) {
return std::lower_bound(std::begin(Range), std::end(Range), I);
auto lower_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
return std::lower_bound(adl_begin(Range), adl_end(Range), I);
}
/// \brief Given a range of type R, iterate the entire range and return a
@ -866,7 +911,7 @@ auto lower_bound(R &&Range, ForwardIt I) -> decltype(std::begin(Range)) {
template <unsigned Size, typename R>
SmallVector<typename std::remove_const<detail::ValueOfRange<R>>::type, Size>
to_vector(R &&Range) {
return {std::begin(Range), std::end(Range)};
return {adl_begin(Range), adl_end(Range)};
}
/// Provide a container algorithm similar to C++ Library Fundamentals v2's

View File

@ -252,20 +252,20 @@ TEST(STLExtrasTest, CountAdaptor) {
EXPECT_EQ(3, count(v, 1));
EXPECT_EQ(2, count(v, 2));
EXPECT_EQ(1, count(v, 3));
EXPECT_EQ(1, count(v, 4));
}
TEST(STLExtrasTest, for_each) {
std::vector<int> v{ 0, 1, 2, 3, 4 };
int count = 0;
llvm::for_each(v, [&count](int) { ++count; });
EXPECT_EQ(5, count);
}
TEST(STLExtrasTest, ToVector) {
std::vector<char> v = {'a', 'b', 'c'};
auto Enumerated = to_vector<4>(enumerate(v));
EXPECT_EQ(1, count(v, 4));
}
TEST(STLExtrasTest, for_each) {
std::vector<int> v{0, 1, 2, 3, 4};
int count = 0;
llvm::for_each(v, [&count](int) { ++count; });
EXPECT_EQ(5, count);
}
TEST(STLExtrasTest, ToVector) {
std::vector<char> v = {'a', 'b', 'c'};
auto Enumerated = to_vector<4>(enumerate(v));
ASSERT_EQ(3u, Enumerated.size());
for (size_t I = 0; I < v.size(); ++I) {
EXPECT_EQ(I, Enumerated[I].index());
@ -326,4 +326,42 @@ TEST(STLExtrasTest, EraseIf) {
EXPECT_EQ(7, V[3]);
}
namespace some_namespace {
struct some_struct {
std::vector<int> data;
std::string swap_val;
};
std::vector<int>::const_iterator begin(const some_struct &s) {
return s.data.begin();
}
std::vector<int>::const_iterator end(const some_struct &s) {
return s.data.end();
}
void swap(some_struct &lhs, some_struct &rhs) {
// make swap visible as non-adl swap would even seem to
// work with std::swap which defaults to moving
lhs.swap_val = "lhs";
rhs.swap_val = "rhs";
}
} // namespace some_namespace
TEST(STLExtrasTest, ADLTest) {
some_namespace::some_struct s{{1, 2, 3, 4, 5}, ""};
some_namespace::some_struct s2{{2, 4, 6, 8, 10}, ""};
EXPECT_EQ(*adl_begin(s), 1);
EXPECT_EQ(*(adl_end(s) - 1), 5);
adl_swap(s, s2);
EXPECT_EQ(s.swap_val, "lhs");
EXPECT_EQ(s2.swap_val, "rhs");
int count = 0;
llvm::for_each(s, [&count](int) { ++count; });
EXPECT_EQ(5, count);
}
} // namespace