forked from OSchip/llvm-project
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:
parent
0282966e38
commit
2bc260aba2
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue