userver: userver/utils/algo.hpp Source File
Loading...
Searching...
No Matches
algo.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/algo.hpp
4/// @brief Small useful algorithms.
5/// @ingroup userver_universal
6
7#include <algorithm>
8#include <cstddef>
9#include <iterator>
10#include <optional>
11#include <string>
12#include <string_view>
13#include <type_traits>
14#include <utility>
15
16#include <userver/utils/checked_pointer.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace utils {
21
22/// @brief Concatenates multiple `std::string_view`-convertible items
23template <typename ResultString = std::string, typename... Strings>
24ResultString StrCat(const Strings&... strings) {
25 return [](auto... string_views) {
26 std::size_t result_size = 0;
27 ((result_size += string_views.size()), ...);
28
29 ResultString result;
30 result.reserve(result_size);
31 (result.append(string_views), ...);
32 return result;
33 }(std::string_view{strings}...);
34}
35
36/// @brief Returns nullptr if no key in associative container, otherwise
37/// returns pointer to value.
38template <class Map, class Key>
39auto* FindOrNullptr(Map& map, const Key& key) {
40 const auto it = map.find(key);
41 return (it == map.end() ? nullptr : &it->second);
42}
43
44/// @brief Returns default value if no key in associative container, otherwise
45/// returns a copy of the stored value.
46template <class Map, class Key, class Default>
47typename Map::mapped_type FindOrDefault(Map& map, const Key& key,
48 Default&& def) {
49 const auto* ptr = utils::FindOrNullptr(map, key);
50 if (!ptr) {
51 return {std::forward<Default>(def)};
52 }
53 return *ptr;
54}
55
56/// @brief Returns default value if no key in associative container, otherwise
57/// returns a copy of the stored value.
58template <class Map, class Key>
59typename Map::mapped_type FindOrDefault(Map& map, const Key& key) {
60 const auto ptr = USERVER_NAMESPACE::utils::FindOrNullptr(map, key);
61 if (!ptr) {
62 return {};
63 }
64 return *ptr;
65}
66
67/// @brief Returns std::nullopt if no key in associative container, otherwise
68/// returns std::optional with a copy of value
69template <class Map, class Key>
70std::optional<typename Map::mapped_type> FindOptional(Map& map,
71 const Key& key) {
72 const auto ptr = utils::FindOrNullptr(map, key);
73 if (!ptr) {
74 return std::nullopt;
75 }
76 return {*ptr};
77}
78
79/// @brief Searches a map for an element and return a checked pointer to
80/// the found element
81template <typename Map, typename Key>
82auto CheckedFind(Map& map, const Key& key)
83 -> decltype(utils::MakeCheckedPtr(&map.find(key)->second)) {
84 if (auto f = map.find(key); f != map.end()) {
85 return utils::MakeCheckedPtr(&f->second);
86 }
87 return {nullptr};
88}
89
90/// @brief Converts one container type to another
91template <class ToContainer, class FromContainer>
92ToContainer AsContainer(FromContainer&& container) {
93 if constexpr (std::is_rvalue_reference_v<decltype(container)>) {
94 return ToContainer(std::make_move_iterator(std::begin(container)),
95 std::make_move_iterator(std::end(container)));
96 } else {
97 return ToContainer(std::begin(container), std::end(container));
98 }
99}
100
101namespace impl {
102template <typename Container, typename = void>
103struct HasKeyType : std::false_type {};
104
105template <typename Container>
106struct HasKeyType<Container, std::void_t<typename Container::key_type>>
107 : std::true_type {};
108
109template <typename Container>
110inline constexpr bool kHasKeyType = HasKeyType<Container>::value;
111} // namespace impl
112
113/// @brief Erased elements and returns number of deleted elements
114template <class Container, class Pred>
115auto EraseIf(Container& container, Pred pred) {
116 if constexpr (impl::kHasKeyType<Container>) {
117 auto old_size = container.size();
118 for (auto it = std::begin(container), last = std::end(container);
119 it != last;) {
120 if (pred(*it)) {
121 it = container.erase(it);
122 } else {
123 ++it;
124 }
125 }
126 return old_size - container.size();
127 } else {
128 auto it = std::remove_if(std::begin(container), std::end(container), pred);
129 const auto removed = std::distance(it, std::end(container));
130 container.erase(it, std::end(container));
131 return removed;
132 }
133}
134
135/// @brief Erased elements and returns number of deleted elements
136template <class Container, class T>
138 if constexpr (impl::kHasKeyType<Container>) {
139 return container.erase(elem);
140 } else {
141 // NOLINTNEXTLINE(readability-qualified-auto)
142 auto it = std::remove(std::begin(container), std::end(container), elem);
143 const auto removed = std::distance(it, std::end(container));
144 container.erase(it, std::end(container));
145 return removed;
146 }
147}
148
149/// @brief returns true if there is an element in container which satisfies
150/// the predicate
151template <typename Container, typename Pred>
152bool ContainsIf(const Container& container, Pred pred) {
153 return std::find_if(std::begin(container), std::end(container), pred) !=
154 std::end(container);
155}
156
157} // namespace utils
158
159USERVER_NAMESPACE_END