1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #pragma once
16
17 #include "base/TypeTraits.h"
18
19 #include <initializer_list>
20 #include <set>
21 #include <map>
22 #include <unordered_map>
23 #include <unordered_set>
24 #include <utility>
25
26 // A set of convenience functions for map and set lookups. They allow a simpler
27 // syntax, e.g.
28 // if (auto val = find(map, "key")) {
29 // <process the value>
30 // }
31 // ... or
32 // auto value = find(funcThatReturnsMap(), "other_key");
33 // if (!value) ...
34 //
35 // Note: these don't work for multimaps, as there's no single value
36 // to return (and, more importantly, as those are completely useless).
37
38 namespace android {
39 namespace base {
40
41 // Helper predicates that check if the template argument is a map / set /
42 // a mutlikey collection of any kind.
43 // These are used as a constraints for the lookup functions to get better error
44 // messages if the arguments don't support the map interface.
45 template <class T>
46 using is_any_map = std::integral_constant<
47 bool,
48 is_template_instantiation_of<T, std::map>::value ||
49 is_template_instantiation_of<T, std::unordered_map>::value>;
50
51 template <class T>
52 using is_any_set = std::integral_constant<
53 bool,
54 is_template_instantiation_of<T, std::set>::value ||
55 is_template_instantiation_of<T, std::unordered_set>::value>;
56
57 template <class T>
58 using is_any_multikey = std::integral_constant<
59 bool,
60 is_template_instantiation_of<T, std::multimap>::value ||
61 is_template_instantiation_of<T, std::unordered_multimap>::value ||
62 is_template_instantiation_of<T, std::multiset>::value ||
63 is_template_instantiation_of<T, std::unordered_multiset>::value>;
64
65 template <class T, class = enable_if<is_any_map<T>>>
find(const T & map,const typename T::key_type & key)66 const typename T::mapped_type* find(const T& map,
67 const typename T::key_type& key) {
68 const auto it = map.find(key);
69 if (it == map.end()) {
70 return nullptr;
71 }
72
73 return &it->second;
74 }
75
76 // Version that returns a modifiable value.
77 template <class T, class = enable_if<is_any_map<T>>>
find(T & map,const typename T::key_type & key)78 typename T::mapped_type* find(T& map, const typename T::key_type& key) {
79 auto it = map.find(key);
80 if (it == map.end()) {
81 return nullptr;
82 }
83
84 return &it->second;
85 }
86
87 // Version with a default, returns a _copy_ because of the possible fallback
88 // to a default - it might be destroyed after the call.
89 template <class T,
90 class U = typename T::mapped_type,
91 class = enable_if_c<
92 is_any_map<T>::value &&
93 std::is_convertible<U, typename T::mapped_type>::value>>
94 typename T::mapped_type findOrDefault(const T& map,
95 const typename T::key_type& key,
96 U&& defaultVal = {}) {
97 if (auto valPtr = find(map, key)) {
98 return *valPtr;
99 }
100 return std::forward<U>(defaultVal);
101 }
102
103 // Version that finds the first of the values passed in |keys| in the order they
104 // are passed. E.g., the following code finds '2' as the first value in |keys|:
105 // set<int> s = {1, 2, 3};
106 // auto val = findFirstOf(s, {2, 1});
107 // EXPECT_EQ(2, *val);
108 template <class T, class = enable_if<is_any_map<T>>>
findFirstOf(const T & map,std::initializer_list<typename T::key_type> keys)109 const typename T::mapped_type* findFirstOf(
110 const T& map,
111 std::initializer_list<typename T::key_type> keys) {
112 for (const auto& key : keys) {
113 if (const auto valPtr = find(map, key)) {
114 return valPtr;
115 }
116 }
117 return nullptr;
118 }
119
120 template <class T, class = enable_if<is_any_map<T>>>
findFirstOf(T & map,std::initializer_list<typename T::key_type> keys)121 typename T::mapped_type* findFirstOf(
122 T& map,
123 std::initializer_list<typename T::key_type> keys) {
124 for (const auto& key : keys) {
125 if (const auto valPtr = find(map, key)) {
126 return valPtr;
127 }
128 }
129 return nullptr;
130 }
131
132 // Version that finds first of the passed |key| values or returns the
133 // |defaultVal| if none were found.
134 template <class T,
135 class U,
136 class = enable_if_c<
137 is_any_map<T>::value &&
138 std::is_convertible<U, typename T::mapped_type>::value>>
findFirstOfOrDefault(const T & map,std::initializer_list<typename T::key_type> keys,U && defaultVal)139 typename T::mapped_type findFirstOfOrDefault(
140 const T& map,
141 std::initializer_list<typename T::key_type> keys,
142 U&& defaultVal) {
143 if (const auto valPtr = findFirstOf(map, keys)) {
144 return *valPtr;
145 }
146 return std::forward<U>(defaultVal);
147 }
148
149 template <class T,
150 class = enable_if_c<is_any_map<T>::value || is_any_set<T>::value ||
151 is_any_multikey<T>::value>>
contains(const T & c,const typename T::key_type & key)152 bool contains(const T& c, const typename T::key_type& key) {
153 const auto it = c.find(key);
154 return it != c.end();
155 }
156
157 template <class T,
158 class = enable_if_c<is_any_map<T>::value || is_any_set<T>::value ||
159 is_any_multikey<T>::value>>
containsAnyOf(const T & c,std::initializer_list<typename T::key_type> keys)160 bool containsAnyOf(const T& c,
161 std::initializer_list<typename T::key_type> keys) {
162 for (const auto& key : keys) {
163 if (contains(c, key)) {
164 return true;
165 }
166 }
167 return false;
168 }
169
170 } // namespace base
171 } // namespace android
172