[PATCH] D146061: [ADT] Make llvm::is_contained call member `contains` when available

Jakub Kuderski via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 14 09:15:08 PDT 2023


kuhar created this revision.
kuhar added reviewers: kazu, dblaikie, zero9178, MaskRay.
Herald added a subscriber: hanchung.
Herald added a project: All.
kuhar requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

This makes it so that calling `llvm::is_contained` no longer degrades
performance over member contains, even though both have almost identical
names. This would be the case in most set/map classes that can check for
an element being present in O(1) or O(log n) time vs. linear scan with
`std::find`.

I also considered detecting member contains and triggering a
`static_assert` instead, but decied against it because it's just as easy
to do the right thing and call `.contains`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D146061

Files:
  llvm/include/llvm/ADT/STLExtras.h
  llvm/unittests/ADT/STLExtrasTest.cpp


Index: llvm/unittests/ADT/STLExtrasTest.cpp
===================================================================
--- llvm/unittests/ADT/STLExtrasTest.cpp
+++ llvm/unittests/ADT/STLExtrasTest.cpp
@@ -1029,6 +1029,26 @@
   static_assert(!is_contained({1, 2, 3, 4}, 5), "It's not there :(");
 }
 
+TEST(STLExtrasTest, IsContainedMemberContains) {
+  // Check that `llvm::is_contained` uses the `.contains()` when available.
+  struct Foo {
+    bool contains(int) const {
+      ++NumContainsCalls;
+      return ContainsResult;
+    }
+    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, addEnumValues) {
   enum A { Zero = 0, One = 1 };
   enum B { IntMax = INT_MAX, ULongLongMax = ULLONG_MAX };
Index: llvm/include/llvm/ADT/STLExtras.h
===================================================================
--- llvm/include/llvm/ADT/STLExtras.h
+++ llvm/include/llvm/ADT/STLExtras.h
@@ -1906,11 +1906,24 @@
   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;
+} // namespace detail
+
+/// Returns true if \p Element is found in \p Range. This either calls
+/// `.contains()` for range types `R` that define it, or `llvm::find`.
 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
+    return llvm::find(Range, Element) != adl_end(Range);
 }
 
 /// Returns true iff \p Element exists in \p Set. This overload takes \p Set as


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D146061.505137.patch
Type: text/x-patch
Size: 2312 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20230314/519967ca/attachment.bin>


More information about the llvm-commits mailing list