return std::move(adl_begin(Range), adl_end(Range), Out);
}
-/// Wrapper function around std::find to detect if an element exists
-/// in a container.
+namespace detail {
+template <typename Range, typename Element>
+using check_has_member_contains_t =
+ decltype(std::declval<Range &>().contains(std::declval<const Element &>()));
+
+template <typename Range, typename Element>
+static constexpr bool HasMemberContains =
+ is_detected<check_has_member_contains_t, Range, Element>::value;
+
+template <typename Range, typename Element>
+using check_has_member_find_t =
+ decltype(std::declval<Range &>().find(std::declval<const Element &>()) !=
+ std::declval<Range &>().end());
+
+template <typename Range, typename Element>
+static constexpr bool HasMemberFind =
+ is_detected<check_has_member_find_t, Range, Element>::value;
+
+} // namespace detail
+
+/// Returns true if \p Element is found in \p Range. Delegates the check to
+/// either `.contains(Element)`, `.find(Element)`, or `std::find`, in this
+/// order of preference. This is intended as the canonical way to check if an
+/// element exists in a range in generic code or range type that does not
+/// expose a `.contains(Element)` member.
template <typename R, typename E>
bool is_contained(R &&Range, const E &Element) {
- return std::find(adl_begin(Range), adl_end(Range), Element) != adl_end(Range);
+ if constexpr (detail::HasMemberContains<R, E>)
+ return Range.contains(Element);
+ else if constexpr (detail::HasMemberFind<R, E>)
+ return Range.find(Element) != Range.end();
+ else
+ return std::find(adl_begin(Range), adl_end(Range), Element) !=
+ adl_end(Range);
}
/// Returns true iff \p Element exists in \p Set. This overload takes \p Set as
// Check the parent loop pointer.
if (ParentLoop) {
- assert(is_contained(*ParentLoop, this) &&
+ assert(is_contained(ParentLoop->getSubLoops(), this) &&
"Loop is not a subloop of its parent!");
}
#endif
static_assert(!is_contained({1, 2, 3, 4}, 5), "It's not there :(");
}
+TEST(STLExtrasTest, IsContainedMemberContains) {
+ // Check that `llvm::is_contained` uses the member `.contains()` when
+ // available. Check that `.contains()` is preferred over `.find()`.
+ struct Foo {
+ bool contains(int) const {
+ ++NumContainsCalls;
+ return ContainsResult;
+ }
+ int *begin() { return nullptr; }
+ int *end() { return nullptr; }
+ int *find(int) { return nullptr; }
+
+ bool ContainsResult = false;
+ mutable unsigned NumContainsCalls = 0;
+ } Container;
+
+ EXPECT_EQ(Container.NumContainsCalls, 0u);
+ EXPECT_FALSE(is_contained(Container, 1));
+ EXPECT_EQ(Container.NumContainsCalls, 1u);
+
+ Container.ContainsResult = true;
+ EXPECT_TRUE(is_contained(Container, 1));
+ EXPECT_EQ(Container.NumContainsCalls, 2u);
+}
+
+TEST(STLExtrasTest, IsContainedMemberFind) {
+ // Check that `llvm::is_contained` uses the member `.find(x)` when available.
+ struct Foo {
+ auto begin() { return Data.begin(); }
+ auto end() { return Data.end(); }
+ auto find(int X) {
+ ++NumFindCalls;
+ return std::find(begin(), end(), X);
+ }
+
+ std::vector<int> Data;
+ mutable unsigned NumFindCalls = 0;
+ } Container;
+
+ Container.Data = {1, 2, 3};
+
+ EXPECT_EQ(Container.NumFindCalls, 0u);
+ EXPECT_TRUE(is_contained(Container, 1));
+ EXPECT_TRUE(is_contained(Container, 3));
+ EXPECT_EQ(Container.NumFindCalls, 2u);
+
+ EXPECT_FALSE(is_contained(Container, 4));
+ EXPECT_EQ(Container.NumFindCalls, 3u);
+}
+
TEST(STLExtrasTest, addEnumValues) {
enum A { Zero = 0, One = 1 };
enum B { IntMax = INT_MAX, ULongLongMax = ULLONG_MAX };