From 94a77ab3adee9f3ab184e23fe0c38d4cb5086635 Mon Sep 17 00:00:00 2001
From: James Gill <jagill@fb.com>
Date: Fri, 15 Mar 2019 06:13:18 -0700
Subject: [PATCH] Implement c++20 contains method on F14Map and F14Set

Summary:
contains() is part of the c++20 spec.  This implements it, and
refactors count() to use contains().

Reviewed By: nbronson

Differential Revision: D14426805

fbshipit-source-id: 2435124e9c0a8eeb4e53109f0ddac884c227c781
---
 folly/container/F14Map.h            | 14 ++++++++++++--
 folly/container/F14Set.h            | 14 ++++++++++++--
 folly/container/test/F14MapTest.cpp | 16 ++++++++++++++++
 folly/container/test/F14SetTest.cpp | 16 ++++++++++++++++
 4 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/folly/container/F14Map.h b/folly/container/F14Map.h
index b91594bbb..bc6452dfe 100644
--- a/folly/container/F14Map.h
+++ b/folly/container/F14Map.h
@@ -748,13 +748,13 @@ class F14BasicMap {
   }
 
   FOLLY_ALWAYS_INLINE size_type count(key_type const& key) const {
-    return table_.find(key).atEnd() ? 0 : 1;
+    return contains(key) ? 1 : 0;
   }
 
   template <typename K>
   FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count(
       K const& key) const {
-    return table_.find(key).atEnd() ? 0 : 1;
+    return contains(key) ? 1 : 0;
   }
 
   // prehash(key) does the work of evaluating hash_function()(key)
@@ -823,6 +823,16 @@ class F14BasicMap {
     return table_.makeConstIter(table_.find(token, key));
   }
 
+  FOLLY_ALWAYS_INLINE bool contains(key_type const& key) const {
+    return !table_.find(key).atEnd();
+  }
+
+  template <typename K>
+  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains(
+      K const& key) const {
+    return !table_.find(key).atEnd();
+  }
+
   std::pair<iterator, iterator> equal_range(key_type const& key) {
     return equal_range(*this, key);
   }
diff --git a/folly/container/F14Set.h b/folly/container/F14Set.h
index 78fdd3894..da99772f7 100644
--- a/folly/container/F14Set.h
+++ b/folly/container/F14Set.h
@@ -530,13 +530,13 @@ class F14BasicSet {
   //// PUBLIC - Lookup
 
   FOLLY_ALWAYS_INLINE size_type count(key_type const& key) const {
-    return table_.find(key).atEnd() ? 0 : 1;
+    return contains(key) ? 1 : 0;
   }
 
   template <typename K>
   FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count(
       K const& key) const {
-    return table_.find(key).atEnd() ? 0 : 1;
+    return contains(key) ? 1 : 0;
   }
 
   // prehash(key) does the work of evaluating hash_function()(key)
@@ -605,6 +605,16 @@ class F14BasicSet {
     return table_.makeIter(table_.find(token, key));
   }
 
+  FOLLY_ALWAYS_INLINE bool contains(key_type const& key) const {
+    return !table_.find(key).atEnd();
+  }
+
+  template <typename K>
+  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains(
+      K const& key) const {
+    return !table_.find(key).atEnd();
+  }
+
   std::pair<iterator, iterator> equal_range(key_type const& key) {
     return equal_range(*this, key);
   }
diff --git a/folly/container/test/F14MapTest.cpp b/folly/container/test/F14MapTest.cpp
index 4e00191af..c942d29b6 100644
--- a/folly/container/test/F14MapTest.cpp
+++ b/folly/container/test/F14MapTest.cpp
@@ -277,6 +277,8 @@ void runSimple() {
   EXPECT_EQ(h8.size(), 2);
   EXPECT_EQ(h8.count(s("abc")), 1);
   EXPECT_EQ(h8.count(s("xyz")), 0);
+  EXPECT_TRUE(h8.contains(s("abc")));
+  EXPECT_FALSE(h8.contains(s("xyz")));
 
   EXPECT_TRUE(h7 != h8);
   EXPECT_TRUE(h8 != h9);
@@ -294,6 +296,10 @@ void runSimple() {
     EXPECT_EQ(h5.count(k), 1);
     EXPECT_EQ(h6.count(k), 1);
     EXPECT_EQ(h8.count(k), 1);
+    EXPECT_TRUE(h4.contains(k));
+    EXPECT_TRUE(h5.contains(k));
+    EXPECT_TRUE(h6.contains(k));
+    EXPECT_TRUE(h8.contains(k));
   }
 
   EXPECT_TRUE(h2 == h7);
@@ -485,6 +491,8 @@ void runRandom() {
             EXPECT_EQ(t->second.val_, r->second.val_);
           }
           EXPECT_EQ(t0.count(k), r0.count(k));
+          // TODO: When std::unordered_map supports c++20:
+          // EXPECT_EQ(t0.contains(k), r0.contains(k));
         } else if (pct < 60) {
           // equal_range
           auto t = t0.equal_range(k);
@@ -1183,10 +1191,13 @@ void runInsertAndEmplace(std::string const& name) {
   M m;
   typename M::key_type k;
   EXPECT_EQ(m.count(k), 0);
+  EXPECT_FALSE(m.contains(k));
   m.emplace();
   EXPECT_EQ(m.count(k), 1);
+  EXPECT_TRUE(m.contains(k));
   m.emplace();
   EXPECT_EQ(m.count(k), 1);
+  EXPECT_TRUE(m.contains(k));
 }
 
 TEST(F14ValueMap, destructuring) {
@@ -1308,6 +1319,10 @@ TEST(F14ValueMap, heterogeneousLookup) {
     EXPECT_TRUE(ref.end() == ref.find(ref.prehash(buddy), buddy));
     EXPECT_EQ(hello, ref.find(ref.prehash(hello), hello)->first);
 
+    // contains
+    EXPECT_FALSE(ref.contains(buddy));
+    EXPECT_TRUE(ref.contains(hello));
+
     // equal_range
     EXPECT_TRUE(std::make_pair(ref.end(), ref.end()) == ref.equal_range(buddy));
     EXPECT_TRUE(
@@ -1403,6 +1418,7 @@ void runHeterogeneousInsertTest() {
 
   resetTracking();
   EXPECT_EQ(map.count(10), 0);
+  EXPECT_FALSE(map.contains(10));
   EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
       << Tracked<1>::counts;
 
diff --git a/folly/container/test/F14SetTest.cpp b/folly/container/test/F14SetTest.cpp
index 679789d64..638196f89 100644
--- a/folly/container/test/F14SetTest.cpp
+++ b/folly/container/test/F14SetTest.cpp
@@ -269,6 +269,8 @@ void runSimple() {
   EXPECT_EQ(h8.size(), 2);
   EXPECT_EQ(h8.count(s("abc")), 1);
   EXPECT_EQ(h8.count(s("xyz")), 0);
+  EXPECT_TRUE(h8.contains(s("abc")));
+  EXPECT_FALSE(h8.contains(s("xyz")));
 
   EXPECT_TRUE(h7 != h8);
   EXPECT_TRUE(h8 != h9);
@@ -286,6 +288,10 @@ void runSimple() {
     EXPECT_EQ(h5.count(k), 1);
     EXPECT_EQ(h6.count(k), 1);
     EXPECT_EQ(h8.count(k), 1);
+    EXPECT_TRUE(h4.contains(k));
+    EXPECT_TRUE(h5.contains(k));
+    EXPECT_TRUE(h6.contains(k));
+    EXPECT_TRUE(h8.contains(k));
   }
 
   h8.clear();
@@ -439,6 +445,8 @@ void runRandom() {
         EXPECT_EQ(*t, *r);
       }
       EXPECT_EQ(t0.count(k), r0.count(k));
+      // TODO: When std::unordered_set supports c++20:
+      // EXPECT_EQ(t0.contains(k), r0.contains(k));
     } else if (pct < 60) {
       // equal_range
       auto t = t0.equal_range(k);
@@ -829,10 +837,13 @@ void runInsertAndEmplace() {
   S s;
   typename S::value_type k;
   EXPECT_EQ(s.count(k), 0);
+  EXPECT_FALSE(s.contains(k));
   s.emplace();
   EXPECT_EQ(s.count(k), 1);
+  EXPECT_TRUE(s.contains(k));
   s.emplace();
   EXPECT_EQ(s.count(k), 1);
+  EXPECT_TRUE(s.contains(k));
 }
 
 TEST(F14ValueSet, destructuring) {
@@ -982,6 +993,10 @@ TEST(F14ValueSet, heterogeneous) {
     EXPECT_TRUE(ref.end() == ref.find(ref.prehash(buddy), buddy));
     EXPECT_EQ(hello, *ref.find(ref.prehash(hello), hello));
 
+    // contains
+    EXPECT_FALSE(ref.contains(buddy));
+    EXPECT_TRUE(ref.contains(hello));
+
     // equal_range
     EXPECT_TRUE(std::make_pair(ref.end(), ref.end()) == ref.equal_range(buddy));
     EXPECT_TRUE(
@@ -1073,6 +1088,7 @@ void runHeterogeneousInsertTest() {
 
   resetTracking();
   EXPECT_EQ(set.count(10), 0);
+  EXPECT_FALSE(set.contains(10));
   EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
       << Tracked<1>::counts;
 
-- 
2.26.2