1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Derived from google3/util/gtl/stl_util.h
7 #ifndef BASE_STL_UTIL_H_
8 #define BASE_STL_UTIL_H_
11 #include <forward_list>
14 #include <type_traits>
17 #include "base/check.h"
18 #include "base/ranges/algorithm.h"
19 #include "third_party/abseil-cpp/absl/types/optional.h"
25 template <typename Iter>
26 constexpr bool IsRandomAccessIter =
27 std::is_same<typename std::iterator_traits<Iter>::iterator_category,
28 std::random_access_iterator_tag>::value;
30 } // namespace internal
32 // Implementation of C++23's std::to_underlying.
34 // Note: This has an additional `std::is_enum<EnumT>` requirement to be SFINAE
35 // friendly prior to C++20.
37 // Reference: https://en.cppreference.com/w/cpp/utility/to_underlying
38 template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>{}>>
39 constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept {
40 return static_cast<std::underlying_type_t<EnumT>>(e);
43 // Returns a const reference to the underlying container of a container adapter.
44 // Works for std::priority_queue, std::queue, and std::stack.
46 const typename A::container_type& GetUnderlyingContainer(const A& adapter) {
47 struct ExposedAdapter : A {
50 return adapter.*&ExposedAdapter::c;
53 // Clears internal memory of an STL object.
54 // STL clear()/reserve(0) does not always free internal memory allocated
55 // This function uses swap/destructor to ensure the internal memory is freed.
57 void STLClearObject(T* obj) {
60 // Sometimes "T tmp" allocates objects with memory (arena implementation?).
61 // Hence using additional reserve(0) even if it doesn't always work.
65 // Counts the number of instances of val in a container.
66 template <typename Container, typename T>
67 typename std::iterator_traits<
68 typename Container::const_iterator>::difference_type
69 STLCount(const Container& container, const T& val) {
70 return std::count(container.begin(), container.end(), val);
73 // O(1) implementation of const casting an iterator for any sequence,
74 // associative or unordered associative container in the STL.
76 // Reference: https://stackoverflow.com/a/10669041
77 template <typename Container,
79 std::enable_if_t<!internal::IsRandomAccessIter<ConstIter>>* = nullptr>
80 constexpr auto ConstCastIterator(Container& c, ConstIter it) {
81 return c.erase(it, it);
84 // Explicit overload for std::forward_list where erase() is named erase_after().
85 template <typename T, typename Allocator>
86 constexpr auto ConstCastIterator(
87 std::forward_list<T, Allocator>& c,
88 typename std::forward_list<T, Allocator>::const_iterator it) {
89 // The erase_after(it, it) trick used below does not work for libstdc++ [1],
90 // thus we need a different way.
91 // TODO(crbug.com/972541): Remove this workaround once libstdc++ is fixed on all
94 // [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90857
95 #if defined(__GLIBCXX__)
96 return c.insert_after(it, {});
98 return c.erase_after(it, it);
102 // Specialized O(1) const casting for random access iterators. This is
103 // necessary, because erase() is either not available (e.g. array-like
104 // containers), or has O(n) complexity (e.g. std::deque or std::vector).
105 template <typename Container,
107 std::enable_if_t<internal::IsRandomAccessIter<ConstIter>>* = nullptr>
108 constexpr auto ConstCastIterator(Container& c, ConstIter it) {
111 return begin(c) + (it - cbegin(c));
116 template <typename Map, typename Key, typename Value>
117 std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
120 auto lower = map.lower_bound(key);
121 if (lower != map.end() && !map.key_comp()(key, lower->first)) {
122 // key already exists, perform assignment.
123 lower->second = std::forward<Value>(value);
124 return {lower, false};
127 // key did not yet exist, insert it.
128 return {map.emplace_hint(lower, std::forward<Key>(key),
129 std::forward<Value>(value)),
133 template <typename Map, typename Key, typename Value>
134 typename Map::iterator InsertOrAssignImpl(Map& map,
135 typename Map::const_iterator hint,
138 auto&& key_comp = map.key_comp();
139 if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
140 if (hint == map.end() || key_comp(key, hint->first)) {
141 // *(hint - 1) < key < *hint => key did not exist and hint is correct.
142 return map.emplace_hint(hint, std::forward<Key>(key),
143 std::forward<Value>(value));
146 if (!key_comp(hint->first, key)) {
147 // key == *hint => key already exists and hint is correct.
148 auto mutable_hint = ConstCastIterator(map, hint);
149 mutable_hint->second = std::forward<Value>(value);
154 // hint was not helpful, dispatch to hintless version.
155 return InsertOrAssignImpl(map, std::forward<Key>(key),
156 std::forward<Value>(value))
160 template <typename Map, typename Key, typename... Args>
161 std::pair<typename Map::iterator, bool> TryEmplaceImpl(Map& map,
164 auto lower = map.lower_bound(key);
165 if (lower != map.end() && !map.key_comp()(key, lower->first)) {
166 // key already exists, do nothing.
167 return {lower, false};
170 // key did not yet exist, insert it.
171 return {map.emplace_hint(lower, std::piecewise_construct,
172 std::forward_as_tuple(std::forward<Key>(key)),
173 std::forward_as_tuple(std::forward<Args>(args)...)),
177 template <typename Map, typename Key, typename... Args>
178 typename Map::iterator TryEmplaceImpl(Map& map,
179 typename Map::const_iterator hint,
182 auto&& key_comp = map.key_comp();
183 if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
184 if (hint == map.end() || key_comp(key, hint->first)) {
185 // *(hint - 1) < key < *hint => key did not exist and hint is correct.
186 return map.emplace_hint(
187 hint, std::piecewise_construct,
188 std::forward_as_tuple(std::forward<Key>(key)),
189 std::forward_as_tuple(std::forward<Args>(args)...));
192 if (!key_comp(hint->first, key)) {
193 // key == *hint => no-op, return correct hint.
194 return ConstCastIterator(map, hint);
198 // hint was not helpful, dispatch to hintless version.
199 return TryEmplaceImpl(map, std::forward<Key>(key),
200 std::forward<Args>(args)...)
204 } // namespace internal
206 // Implementation of C++17's std::map::insert_or_assign as a free function.
207 template <typename Map, typename Value>
208 std::pair<typename Map::iterator, bool>
209 InsertOrAssign(Map& map, const typename Map::key_type& key, Value&& value) {
210 return internal::InsertOrAssignImpl(map, key, std::forward<Value>(value));
213 template <typename Map, typename Value>
214 std::pair<typename Map::iterator, bool>
215 InsertOrAssign(Map& map, typename Map::key_type&& key, Value&& value) {
216 return internal::InsertOrAssignImpl(map, std::move(key),
217 std::forward<Value>(value));
220 // Implementation of C++17's std::map::insert_or_assign with hint as a free
222 template <typename Map, typename Value>
223 typename Map::iterator InsertOrAssign(Map& map,
224 typename Map::const_iterator hint,
225 const typename Map::key_type& key,
227 return internal::InsertOrAssignImpl(map, hint, key,
228 std::forward<Value>(value));
231 template <typename Map, typename Value>
232 typename Map::iterator InsertOrAssign(Map& map,
233 typename Map::const_iterator hint,
234 typename Map::key_type&& key,
236 return internal::InsertOrAssignImpl(map, hint, std::move(key),
237 std::forward<Value>(value));
240 // Implementation of C++17's std::map::try_emplace as a free function.
241 template <typename Map, typename... Args>
242 std::pair<typename Map::iterator, bool>
243 TryEmplace(Map& map, const typename Map::key_type& key, Args&&... args) {
244 return internal::TryEmplaceImpl(map, key, std::forward<Args>(args)...);
247 template <typename Map, typename... Args>
248 std::pair<typename Map::iterator, bool> TryEmplace(Map& map,
249 typename Map::key_type&& key,
251 return internal::TryEmplaceImpl(map, std::move(key),
252 std::forward<Args>(args)...);
255 // Implementation of C++17's std::map::try_emplace with hint as a free
257 template <typename Map, typename... Args>
258 typename Map::iterator TryEmplace(Map& map,
259 typename Map::const_iterator hint,
260 const typename Map::key_type& key,
262 return internal::TryEmplaceImpl(map, hint, key, std::forward<Args>(args)...);
265 template <typename Map, typename... Args>
266 typename Map::iterator TryEmplace(Map& map,
267 typename Map::const_iterator hint,
268 typename Map::key_type&& key,
270 return internal::TryEmplaceImpl(map, hint, std::move(key),
271 std::forward<Args>(args)...);
274 // Returns a new ResultType containing the difference of two sorted containers.
275 template <typename ResultType, typename Arg1, typename Arg2>
276 ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
277 DCHECK(ranges::is_sorted(a1));
278 DCHECK(ranges::is_sorted(a2));
279 ResultType difference;
280 std::set_difference(a1.begin(), a1.end(),
281 a2.begin(), a2.end(),
282 std::inserter(difference, difference.end()));
286 // Returns a new ResultType containing the union of two sorted containers.
287 template <typename ResultType, typename Arg1, typename Arg2>
288 ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
289 DCHECK(ranges::is_sorted(a1));
290 DCHECK(ranges::is_sorted(a2));
292 std::set_union(a1.begin(), a1.end(),
293 a2.begin(), a2.end(),
294 std::inserter(result, result.end()));
298 // Returns a new ResultType containing the intersection of two sorted
300 template <typename ResultType, typename Arg1, typename Arg2>
301 ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
302 DCHECK(ranges::is_sorted(a1));
303 DCHECK(ranges::is_sorted(a2));
305 std::set_intersection(a1.begin(), a1.end(),
306 a2.begin(), a2.end(),
307 std::inserter(result, result.end()));
311 // A helper class to be used as the predicate with |EraseIf| to implement
312 // in-place set intersection. Helps implement the algorithm of going through
313 // each container an element at a time, erasing elements from the first
314 // container if they aren't in the second container. Requires each container be
315 // sorted. Note that the logic below appears inverted since it is returning
316 // whether an element should be erased.
317 template <class Collection>
320 explicit IsNotIn(const Collection& collection)
321 : i_(collection.begin()), end_(collection.end()) {}
323 bool operator()(const typename Collection::value_type& x) {
324 while (i_ != end_ && *i_ < x)
336 typename Collection::const_iterator i_;
337 const typename Collection::const_iterator end_;
340 // Helper for returning the optional value's address, or nullptr.
342 T* OptionalOrNullptr(absl::optional<T>& optional) {
343 return optional.has_value() ? &optional.value() : nullptr;
347 const T* OptionalOrNullptr(const absl::optional<T>& optional) {
348 return optional.has_value() ? &optional.value() : nullptr;
351 // Helper for creating an optional<T> from a potentially nullptr T*.
353 absl::optional<T> OptionalFromPtr(const T* value) {
355 return absl::optional<T>(*value);
356 return absl::nullopt;
361 #endif // BASE_STL_UTIL_H_