Commit b77149f3 authored by jdoerrie's avatar jdoerrie Committed by Commit Bot

[base] Introduce base::Contains

This change introduces base::Contains(), generalizing the existing
base::ContainsKey() and base::ContainsValue(). base::Contains() has
multiple templated overloads, depending on what API the queried
container provides. If possible, it invokes a contains() member function
on the passed in container. If this does not work, it will choose a
member find() function. If such a function is not present either, it will
use std::find() as a fallback.

Bug: 970209
Change-Id: I54fb619b6cf229308a40cc81658bd800c3671d40
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1631406Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#666228}
parent 8bb9a271
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <utility>
#include <vector> #include <vector>
#include "base/logging.h" #include "base/logging.h"
...@@ -45,6 +47,38 @@ constexpr bool IsRandomAccessIter = ...@@ -45,6 +47,38 @@ constexpr bool IsRandomAccessIter =
std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::is_same<typename std::iterator_traits<Iter>::iterator_category,
std::random_access_iterator_tag>::value; std::random_access_iterator_tag>::value;
// Utility type traits used for specializing base::Contains() below.
template <typename Container, typename Element, typename = void>
struct HasFindWithNpos : std::false_type {};
template <typename Container, typename Element>
struct HasFindWithNpos<
Container,
Element,
void_t<decltype(std::declval<const Container&>().find(
std::declval<const Element&>()) != Container::npos)>>
: std::true_type {};
template <typename Container, typename Element, typename = void>
struct HasFindWithEnd : std::false_type {};
template <typename Container, typename Element>
struct HasFindWithEnd<Container,
Element,
void_t<decltype(std::declval<const Container&>().find(
std::declval<const Element&>()) !=
std::declval<const Container&>().end())>>
: std::true_type {};
template <typename Container, typename Element, typename = void>
struct HasContains : std::false_type {};
template <typename Container, typename Element>
struct HasContains<Container,
Element,
void_t<decltype(std::declval<const Container&>().contains(
std::declval<const Element&>()))>> : std::true_type {};
} // namespace internal } // namespace internal
// C++14 implementation of C++17's std::size(): // C++14 implementation of C++17's std::size():
...@@ -140,6 +174,65 @@ STLCount(const Container& container, const T& val) { ...@@ -140,6 +174,65 @@ STLCount(const Container& container, const T& val) {
return std::count(container.begin(), container.end(), val); return std::count(container.begin(), container.end(), val);
} }
// General purpose implementation to check if |container| contains |value|.
template <typename Container,
typename Value,
std::enable_if_t<
!internal::HasFindWithNpos<Container, Value>::value &&
!internal::HasFindWithEnd<Container, Value>::value &&
!internal::HasContains<Container, Value>::value>* = nullptr>
bool Contains(const Container& container, const Value& value) {
using std::begin;
using std::end;
return std::find(begin(container), end(container), value) != end(container);
}
// Specialized Contains() implementation for when |container| has a find()
// member function and a static npos member, but no contains() member function.
template <typename Container,
typename Value,
std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
!internal::HasContains<Container, Value>::value>* =
nullptr>
bool Contains(const Container& container, const Value& value) {
return container.find(value) != Container::npos;
}
// Specialized Contains() implementation for when |container| has a find()
// and end() member function, but no contains() member function.
template <typename Container,
typename Value,
std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
!internal::HasContains<Container, Value>::value>* =
nullptr>
bool Contains(const Container& container, const Value& value) {
return container.find(value) != container.end();
}
// Specialized Contains() implementation for when |container| has a contains()
// member function.
template <
typename Container,
typename Value,
std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
bool Contains(const Container& container, const Value& value) {
return container.contains(value);
}
// Test to see if a set or map contains a particular key.
// Returns true if the key is in the collection.
template <typename Collection, typename Key>
bool ContainsKey(const Collection& collection, const Key& key) {
return Contains(collection, key);
}
// Test to see if a collection like a vector contains a particular value.
// Returns true if the value is in the collection.
template <typename Collection, typename Value>
bool ContainsValue(const Collection& collection, const Value& value) {
return Contains(collection, value);
}
// O(1) implementation of const casting an iterator for any sequence, // O(1) implementation of const casting an iterator for any sequence,
// associative or unordered associative container in the STL. // associative or unordered associative container in the STL.
// //
...@@ -171,26 +264,8 @@ constexpr auto ConstCastIterator(Container& c, ConstIter it) { ...@@ -171,26 +264,8 @@ constexpr auto ConstCastIterator(Container& c, ConstIter it) {
return begin(c) + (it - cbegin(c)); return begin(c) + (it - cbegin(c));
} }
// Test to see if a set or map contains a particular key.
// Returns true if the key is in the collection.
template <typename Collection, typename Key>
bool ContainsKey(const Collection& collection, const Key& key) {
return collection.find(key) != collection.end();
}
namespace internal { namespace internal {
template <typename Collection>
class HasKeyType {
template <typename C>
static std::true_type test(typename C::key_type*);
template <typename C>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<Collection>(nullptr))::value;
};
template <typename Map, typename Key, typename Value> template <typename Map, typename Key, typename Value>
std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map, std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
Key&& key, Key&& key,
...@@ -281,19 +356,6 @@ typename Map::iterator TryEmplaceImpl(Map& map, ...@@ -281,19 +356,6 @@ typename Map::iterator TryEmplaceImpl(Map& map,
} // namespace internal } // namespace internal
// Test to see if a collection like a vector contains a particular value.
// Returns true if the value is in the collection.
// Don't use this on collections such as sets or maps. This is enforced by
// disabling this method if the collection defines a key_type.
template <typename Collection,
typename Value,
typename std::enable_if<!internal::HasKeyType<Collection>::value,
int>::type = 0>
bool ContainsValue(const Collection& collection, const Value& value) {
return std::find(std::begin(collection), std::end(collection), value) !=
std::end(collection);
}
// Implementation of C++17's std::map::insert_or_assign as a free function. // Implementation of C++17's std::map::insert_or_assign as a free function.
template <typename Map, typename Value> template <typename Map, typename Value>
std::pair<typename Map::iterator, bool> std::pair<typename Map::iterator, bool>
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include "base/containers/flat_set.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
...@@ -635,14 +636,39 @@ TEST(Erase, IsNotIn) { ...@@ -635,14 +636,39 @@ TEST(Erase, IsNotIn) {
EXPECT_EQ(expected, lhs); EXPECT_EQ(expected, lhs);
} }
TEST(ContainsValue, OrdinaryArrays) { TEST(STLUtilTest, GenericContains) {
const char allowed_chars[] = {'a', 'b', 'c', 'd'}; const char allowed_chars[] = {'a', 'b', 'c', 'd'};
EXPECT_TRUE(ContainsValue(allowed_chars, 'a'));
EXPECT_FALSE(ContainsValue(allowed_chars, 'z')); EXPECT_TRUE(Contains(allowed_chars, 'a'));
EXPECT_FALSE(ContainsValue(allowed_chars, 0)); EXPECT_FALSE(Contains(allowed_chars, 'z'));
EXPECT_FALSE(Contains(allowed_chars, 0));
const char allowed_chars_including_nul[] = "abcd"; const char allowed_chars_including_nul[] = "abcd";
EXPECT_TRUE(ContainsValue(allowed_chars_including_nul, 0)); EXPECT_TRUE(Contains(allowed_chars_including_nul, 0));
}
TEST(STLUtilTest, ContainsWithFindAndNpos) {
std::string str = "abcd";
EXPECT_TRUE(Contains(str, 'a'));
EXPECT_FALSE(Contains(str, 'z'));
EXPECT_FALSE(Contains(str, 0));
}
TEST(STLUtilTest, ContainsWithFindAndEnd) {
std::set<int> set = {1, 2, 3, 4};
EXPECT_TRUE(Contains(set, 1));
EXPECT_FALSE(Contains(set, 5));
EXPECT_FALSE(Contains(set, 0));
}
TEST(STLUtilTest, ContainsWithContains) {
flat_set<int> set = {1, 2, 3, 4};
EXPECT_TRUE(Contains(set, 1));
EXPECT_FALSE(Contains(set, 5));
EXPECT_FALSE(Contains(set, 0));
} }
TEST(STLUtilTest, InsertOrAssign) { TEST(STLUtilTest, InsertOrAssign) {
......
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using testing::Contains;
namespace base { namespace base {
// It has been observed that calling // It has been observed that calling
...@@ -71,7 +69,7 @@ TEST(PlatformThreadWinTest, ...@@ -71,7 +69,7 @@ TEST(PlatformThreadWinTest,
// NORMAL_PRIORITY_CLASS process. // NORMAL_PRIORITY_CLASS process.
THREAD_PRIORITY_IDLE, internal::kWin7BackgroundThreadModePriority}); THREAD_PRIORITY_IDLE, internal::kWin7BackgroundThreadModePriority});
EXPECT_THAT(kExpectedWin7Priorities, EXPECT_THAT(kExpectedWin7Priorities,
Contains(priority_after_thread_mode_background_begin)); testing::Contains(priority_after_thread_mode_background_begin));
} else { } else {
EXPECT_EQ(priority_after_thread_mode_background_begin, EXPECT_EQ(priority_after_thread_mode_background_begin,
THREAD_PRIORITY_NORMAL); THREAD_PRIORITY_NORMAL);
......
...@@ -21,7 +21,6 @@ using testing::ElementsAre; ...@@ -21,7 +21,6 @@ using testing::ElementsAre;
using testing::Eq; using testing::Eq;
using testing::ByRef; using testing::ByRef;
using testing::IsEmpty; using testing::IsEmpty;
using testing::Contains;
namespace base { namespace base {
namespace trace_event { namespace trace_event {
...@@ -62,7 +61,7 @@ void CheckString(const MemoryAllocatorDump* dump, ...@@ -62,7 +61,7 @@ void CheckString(const MemoryAllocatorDump* dump,
const char* expected_units, const char* expected_units,
const std::string& expected_value) { const std::string& expected_value) {
MemoryAllocatorDump::Entry expected(name, expected_units, expected_value); MemoryAllocatorDump::Entry expected(name, expected_units, expected_value);
EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(expected)))); EXPECT_THAT(dump->entries(), testing::Contains(Eq(ByRef(expected))));
} }
void CheckScalar(const MemoryAllocatorDump* dump, void CheckScalar(const MemoryAllocatorDump* dump,
...@@ -70,7 +69,7 @@ void CheckScalar(const MemoryAllocatorDump* dump, ...@@ -70,7 +69,7 @@ void CheckScalar(const MemoryAllocatorDump* dump,
const char* expected_units, const char* expected_units,
uint64_t expected_value) { uint64_t expected_value) {
MemoryAllocatorDump::Entry expected(name, expected_units, expected_value); MemoryAllocatorDump::Entry expected(name, expected_units, expected_value);
EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(expected)))); EXPECT_THAT(dump->entries(), testing::Contains(Eq(ByRef(expected))));
} }
} // namespace } // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment