Split get_default() into two for deferred default construction
[folly.git] / folly / MapUtil.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <folly/Conv.h>
20 #include <folly/Optional.h>
21 #include <folly/functional/Invoke.h>
22 #include <tuple>
23
24 namespace folly {
25
26 /**
27  * Given a map and a key, return the value corresponding to the key in the map,
28  * or a given default value if the key doesn't exist in the map.
29  */
30 template <typename Map, typename Key>
31 typename Map::mapped_type get_default(const Map& map, const Key& key) {
32   auto pos = map.find(key);
33   return (pos != map.end()) ? (pos->second) : (typename Map::mapped_type{});
34 }
35 template <
36     class Map,
37     typename Key = typename Map::key_type,
38     typename Value = typename Map::mapped_type,
39     typename std::enable_if<!is_invocable<Value>::value>::type* = nullptr>
40 typename Map::mapped_type
41 get_default(const Map& map, const Key& key, Value&& dflt) {
42   auto pos = map.find(key);
43   if (pos != map.end()) {
44     return pos->second;
45   } else {
46     // if elision from function parameters was allowed, then we could make the
47     // third parameter a value parameter and just elide that into the return
48     // value, but sadly that is not allowed (yet)
49     return std::forward<Value>(dflt);
50   }
51 }
52
53 /**
54  * Give a map and a key, return the value corresponding to the key in the map,
55  * or a given default value if the key doesn't exist in the map.
56  */
57 template <
58     class Map,
59     typename Key = typename Map::key_type,
60     typename Func,
61     typename = typename std::enable_if<std::is_convertible<
62         typename std::result_of<Func()>::type,
63         typename Map::mapped_type>::value>::type>
64 typename Map::mapped_type
65 get_default(const Map& map, const Key& key, Func&& dflt) {
66   auto pos = map.find(key);
67   return pos != map.end() ? pos->second : dflt();
68 }
69
70 /**
71  * Given a map and a key, return the value corresponding to the key in the map,
72  * or throw an exception of the specified type.
73  */
74 template <
75     class E = std::out_of_range,
76     class Map,
77     typename Key = typename Map::key_type>
78 const typename Map::mapped_type& get_or_throw(
79     const Map& map,
80     const Key& key,
81     const std::string& exceptionStrPrefix = std::string()) {
82   auto pos = map.find(key);
83   if (pos != map.end()) {
84     return pos->second;
85   }
86   throw E(folly::to<std::string>(exceptionStrPrefix, key));
87 }
88
89 template <
90     class E = std::out_of_range,
91     class Map,
92     typename Key = typename Map::key_type>
93 typename Map::mapped_type& get_or_throw(
94     Map& map,
95     const Key& key,
96     const std::string& exceptionStrPrefix = std::string()) {
97   auto pos = map.find(key);
98   if (pos != map.end()) {
99     return pos->second;
100   }
101   throw E(folly::to<std::string>(exceptionStrPrefix, key));
102 }
103
104 /**
105  * Given a map and a key, return a Optional<V> if the key exists and None if the
106  * key does not exist in the map.
107  */
108 template <class Map, typename Key = typename Map::key_type>
109 folly::Optional<typename Map::mapped_type> get_optional(
110     const Map& map,
111     const Key& key) {
112   auto pos = map.find(key);
113   if (pos != map.end()) {
114     return folly::Optional<typename Map::mapped_type>(pos->second);
115   } else {
116     return folly::none;
117   }
118 }
119
120 /**
121  * Given a map and a key, return a reference to the value corresponding to the
122  * key in the map, or the given default reference if the key doesn't exist in
123  * the map.
124  */
125 template <class Map, typename Key = typename Map::key_type>
126 const typename Map::mapped_type& get_ref_default(
127     const Map& map,
128     const Key& key,
129     const typename Map::mapped_type& dflt) {
130   auto pos = map.find(key);
131   return (pos != map.end() ? pos->second : dflt);
132 }
133
134 /**
135  * Passing a temporary default value returns a dangling reference when it is
136  * returned. Lifetime extension is broken by the indirection.
137  * The caller must ensure that the default value outlives the reference returned
138  * by get_ref_default().
139  */
140 template <class Map, typename Key = typename Map::key_type>
141 const typename Map::mapped_type& get_ref_default(
142     const Map& map,
143     const Key& key,
144     typename Map::mapped_type&& dflt) = delete;
145
146 template <class Map, typename Key = typename Map::key_type>
147 const typename Map::mapped_type& get_ref_default(
148     const Map& map,
149     const Key& key,
150     const typename Map::mapped_type&& dflt) = delete;
151
152 /**
153  * Given a map and a key, return a reference to the value corresponding to the
154  * key in the map, or the given default reference if the key doesn't exist in
155  * the map.
156  */
157 template <
158     class Map,
159     typename Key = typename Map::key_type,
160     typename Func,
161     typename = typename std::enable_if<std::is_convertible<
162         typename std::result_of<Func()>::type,
163         const typename Map::mapped_type&>::value>::type,
164     typename = typename std::enable_if<
165         std::is_reference<typename std::result_of<Func()>::type>::value>::type>
166 const typename Map::mapped_type&
167 get_ref_default(const Map& map, const Key& key, Func&& dflt) {
168   auto pos = map.find(key);
169   return (pos != map.end() ? pos->second : dflt());
170 }
171
172 /**
173  * Given a map and a key, return a pointer to the value corresponding to the
174  * key in the map, or nullptr if the key doesn't exist in the map.
175  */
176 template <class Map, typename Key = typename Map::key_type>
177 const typename Map::mapped_type* get_ptr(const Map& map, const Key& key) {
178   auto pos = map.find(key);
179   return (pos != map.end() ? &pos->second : nullptr);
180 }
181
182 /**
183  * Non-const overload of the above.
184  */
185 template <class Map, typename Key = typename Map::key_type>
186 typename Map::mapped_type* get_ptr(Map& map, const Key& key) {
187   auto pos = map.find(key);
188   return (pos != map.end() ? &pos->second : nullptr);
189 }
190
191 // TODO: Remove the return type computations when clang 3.5 and gcc 5.1 are
192 // the minimum supported versions.
193 namespace detail {
194 template <
195     class T,
196     size_t pathLength,
197     class = typename std::enable_if<(pathLength > 0)>::type>
198 struct NestedMapType {
199   using type = typename NestedMapType<T, pathLength - 1>::type::mapped_type;
200 };
201
202 template <class T>
203 struct NestedMapType<T, 1> {
204   using type = typename T::mapped_type;
205 };
206
207 template <typename... KeysDefault>
208 struct DefaultType;
209
210 template <typename Default>
211 struct DefaultType<Default> {
212   using type = Default;
213 };
214
215 template <typename Key, typename... KeysDefault>
216 struct DefaultType<Key, KeysDefault...> {
217   using type = typename DefaultType<KeysDefault...>::type;
218 };
219
220 template <class... KeysDefault>
221 auto extract_default(const KeysDefault&... keysDefault) ->
222     typename DefaultType<KeysDefault...>::type const& {
223   return std::get<sizeof...(KeysDefault)-1>(std::tie(keysDefault...));
224 }
225 } // namespace detail
226
227 /**
228  * Given a map of maps and a path of keys, return a pointer to the nested value,
229  * or nullptr if the key doesn't exist in the map.
230  */
231 template <class Map, class Key1, class Key2, class... Keys>
232 auto get_ptr(
233     const Map& map,
234     const Key1& key1,
235     const Key2& key2,
236     const Keys&... keys) ->
237     typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type const* {
238   auto pos = map.find(key1);
239   return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr;
240 }
241
242 template <class Map, class Key1, class Key2, class... Keys>
243 auto get_ptr(Map& map, const Key1& key1, const Key2& key2, const Keys&... keys)
244     -> typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type* {
245   auto pos = map.find(key1);
246   return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr;
247 }
248
249 /**
250  * Given a map and a path of keys, return the value corresponding to the nested
251  * value, or a given default value if the path doesn't exist in the map.
252  * The default value is the last parameter, and is copied when returned.
253  */
254 template <
255     class Map,
256     class Key1,
257     class Key2,
258     class... KeysDefault,
259     typename = typename std::enable_if<sizeof...(KeysDefault) != 0>::type>
260 auto get_default(
261     const Map& map,
262     const Key1& key1,
263     const Key2& key2,
264     const KeysDefault&... keysDefault) ->
265     typename detail::NestedMapType<Map, 1 + sizeof...(KeysDefault)>::type {
266   if (const auto* ptr = get_ptr(map, key1)) {
267     return get_default(*ptr, key2, keysDefault...);
268   }
269   return detail::extract_default(keysDefault...);
270 }
271
272 /**
273  * Given a map and a path of keys, return a reference to the value corresponding
274  * to the nested value, or the given default reference if the path doesn't exist
275  * in the map.
276  * The default value is the last parameter, and must be a lvalue reference.
277  */
278 template <
279     class Map,
280     class Key1,
281     class Key2,
282     class... KeysDefault,
283     typename = typename std::enable_if<sizeof...(KeysDefault) != 0>::type,
284     typename = typename std::enable_if<std::is_lvalue_reference<
285         typename detail::DefaultType<KeysDefault...>::type>::value>::type>
286 auto get_ref_default(
287     const Map& map,
288     const Key1& key1,
289     const Key2& key2,
290     KeysDefault&&... keysDefault) ->
291     typename detail::NestedMapType<Map, 1 + sizeof...(KeysDefault)>::type
292     const& {
293   if (const auto* ptr = get_ptr(map, key1)) {
294     return get_ref_default(*ptr, key2, keysDefault...);
295   }
296   return detail::extract_default(keysDefault...);
297 }
298 } // namespace folly