[llvm] r318703 - Add ADL support to range based <algorithm> extensions
David Blaikie via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 20 14:12:55 PST 2017
Author: dblaikie
Date: Mon Nov 20 14:12:55 2017
New Revision: 318703
URL: http://llvm.org/viewvc/llvm-project?rev=318703&view=rev
Log:
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
Modified:
llvm/trunk/include/llvm/ADT/STLExtras.h
llvm/trunk/unittests/ADT/STLExtrasTest.cpp
Modified: llvm/trunk/include/llvm/ADT/STLExtras.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/STLExtras.h?rev=318703&r1=318702&r2=318703&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/STLExtras.h (original)
+++ llvm/trunk/include/llvm/ADT/STLExtras.h Mon Nov 20 14:12:55 2017
@@ -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)
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
Modified: llvm/trunk/unittests/ADT/STLExtrasTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/STLExtrasTest.cpp?rev=318703&r1=318702&r2=318703&view=diff
==============================================================================
--- llvm/trunk/unittests/ADT/STLExtrasTest.cpp (original)
+++ llvm/trunk/unittests/ADT/STLExtrasTest.cpp Mon Nov 20 14:12:55 2017
@@ -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
More information about the llvm-commits
mailing list