1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_CONTAINERS_CONTAINS_H_ 6*6777b538SAndroid Build Coastguard Worker #define BASE_CONTAINERS_CONTAINS_H_ 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard Worker #include <type_traits> 9*6777b538SAndroid Build Coastguard Worker #include <utility> 10*6777b538SAndroid Build Coastguard Worker 11*6777b538SAndroid Build Coastguard Worker #include "base/ranges/algorithm.h" 12*6777b538SAndroid Build Coastguard Worker #include "base/ranges/ranges.h" 13*6777b538SAndroid Build Coastguard Worker 14*6777b538SAndroid Build Coastguard Worker namespace base { 15*6777b538SAndroid Build Coastguard Worker 16*6777b538SAndroid Build Coastguard Worker namespace internal { 17*6777b538SAndroid Build Coastguard Worker 18*6777b538SAndroid Build Coastguard Worker // Small helper to detect whether a given type has a nested `key_type` typedef. 19*6777b538SAndroid Build Coastguard Worker // Used below to catch misuses of the API for associative containers. 20*6777b538SAndroid Build Coastguard Worker template <typename T, typename SFINAE = void> 21*6777b538SAndroid Build Coastguard Worker struct HasKeyType : std::false_type {}; 22*6777b538SAndroid Build Coastguard Worker 23*6777b538SAndroid Build Coastguard Worker template <typename T> 24*6777b538SAndroid Build Coastguard Worker struct HasKeyType<T, std::void_t<typename T::key_type>> : std::true_type {}; 25*6777b538SAndroid Build Coastguard Worker 26*6777b538SAndroid Build Coastguard Worker // Probe whether a `contains` member function exists and return the result of 27*6777b538SAndroid Build Coastguard Worker // `container.contains(value)` if this is a valid expression. This is the 28*6777b538SAndroid Build Coastguard Worker // highest priority option. 29*6777b538SAndroid Build Coastguard Worker template <typename Container, typename Value> 30*6777b538SAndroid Build Coastguard Worker constexpr auto ContainsImpl(const Container& container, 31*6777b538SAndroid Build Coastguard Worker const Value& value, 32*6777b538SAndroid Build Coastguard Worker priority_tag<2>) 33*6777b538SAndroid Build Coastguard Worker -> decltype(container.contains(value)) { 34*6777b538SAndroid Build Coastguard Worker return container.contains(value); 35*6777b538SAndroid Build Coastguard Worker } 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Worker // Probe whether a `find` member function exists and whether its return value 38*6777b538SAndroid Build Coastguard Worker // can be compared with `container.end()`. Intended for STL style maps and sets 39*6777b538SAndroid Build Coastguard Worker // that lack a `contains` member function. 40*6777b538SAndroid Build Coastguard Worker template <typename Container, typename Value> 41*6777b538SAndroid Build Coastguard Worker constexpr auto ContainsImpl(const Container& container, 42*6777b538SAndroid Build Coastguard Worker const Value& value, 43*6777b538SAndroid Build Coastguard Worker priority_tag<1>) 44*6777b538SAndroid Build Coastguard Worker -> decltype(container.find(value) != container.end()) { 45*6777b538SAndroid Build Coastguard Worker return container.find(value) != container.end(); 46*6777b538SAndroid Build Coastguard Worker } 47*6777b538SAndroid Build Coastguard Worker 48*6777b538SAndroid Build Coastguard Worker // Probe whether a `find` member function exists and whether its return value 49*6777b538SAndroid Build Coastguard Worker // can be compared with `Container::npos`. Intended for STL style strings that 50*6777b538SAndroid Build Coastguard Worker // lack a `contains` member function. 51*6777b538SAndroid Build Coastguard Worker template <typename Container, typename Value> 52*6777b538SAndroid Build Coastguard Worker constexpr auto ContainsImpl(const Container& container, 53*6777b538SAndroid Build Coastguard Worker const Value& value, 54*6777b538SAndroid Build Coastguard Worker priority_tag<1>) 55*6777b538SAndroid Build Coastguard Worker -> decltype(container.find(value) != Container::npos) { 56*6777b538SAndroid Build Coastguard Worker return container.find(value) != Container::npos; 57*6777b538SAndroid Build Coastguard Worker } 58*6777b538SAndroid Build Coastguard Worker 59*6777b538SAndroid Build Coastguard Worker // Generic fallback option, using a linear search over `container` to find 60*6777b538SAndroid Build Coastguard Worker // `value`. Has the lowest priority. This will not compile for associative 61*6777b538SAndroid Build Coastguard Worker // containers, as this likely is a performance bug. 62*6777b538SAndroid Build Coastguard Worker template <typename Container, typename Value> 63*6777b538SAndroid Build Coastguard Worker constexpr bool ContainsImpl(const Container& container, 64*6777b538SAndroid Build Coastguard Worker const Value& value, 65*6777b538SAndroid Build Coastguard Worker priority_tag<0>) { 66*6777b538SAndroid Build Coastguard Worker static_assert( 67*6777b538SAndroid Build Coastguard Worker !HasKeyType<Container>::value, 68*6777b538SAndroid Build Coastguard Worker "Error: About to perform linear search on an associative container. " 69*6777b538SAndroid Build Coastguard Worker "Either use a more generic comparator (e.g. std::less<>) or, if a linear " 70*6777b538SAndroid Build Coastguard Worker "search is desired, provide an explicit projection parameter."); 71*6777b538SAndroid Build Coastguard Worker return ranges::find(container, value) != ranges::end(container); 72*6777b538SAndroid Build Coastguard Worker } 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Worker } // namespace internal 75*6777b538SAndroid Build Coastguard Worker 76*6777b538SAndroid Build Coastguard Worker // A general purpose utility to check whether `container` contains `value`. This 77*6777b538SAndroid Build Coastguard Worker // will probe whether a `contains` or `find` member function on `container` 78*6777b538SAndroid Build Coastguard Worker // exists, and fall back to a generic linear search over `container`. 79*6777b538SAndroid Build Coastguard Worker template <typename Container, typename Value> 80*6777b538SAndroid Build Coastguard Worker constexpr bool Contains(const Container& container, const Value& value) { 81*6777b538SAndroid Build Coastguard Worker return internal::ContainsImpl(container, value, internal::priority_tag<2>()); 82*6777b538SAndroid Build Coastguard Worker } 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Worker // Overload that allows to provide an additional projection invocable. This 85*6777b538SAndroid Build Coastguard Worker // projection will be applied to every element in `container` before comparing 86*6777b538SAndroid Build Coastguard Worker // it with `value`. This will always perform a linear search. 87*6777b538SAndroid Build Coastguard Worker template <typename Container, typename Value, typename Proj> 88*6777b538SAndroid Build Coastguard Worker constexpr bool Contains(const Container& container, 89*6777b538SAndroid Build Coastguard Worker const Value& value, 90*6777b538SAndroid Build Coastguard Worker Proj proj) { 91*6777b538SAndroid Build Coastguard Worker return ranges::find(container, value, std::move(proj)) != 92*6777b538SAndroid Build Coastguard Worker ranges::end(container); 93*6777b538SAndroid Build Coastguard Worker } 94*6777b538SAndroid Build Coastguard Worker 95*6777b538SAndroid Build Coastguard Worker } // namespace base 96*6777b538SAndroid Build Coastguard Worker 97*6777b538SAndroid Build Coastguard Worker #endif // BASE_CONTAINERS_CONTAINS_H_ 98