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 <memory>
11#include <optional>
12#include <string>
13#include <string_view>
14#include <type_traits>
15#include <utility>
16
17#include <userver/utils/checked_pointer.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21/// @brief General-purpose utilities used across userver libraries.
22namespace utils {
23
24/// @brief Concatenates multiple `std::string_view`-convertible items
25template <typename ResultString = std::string, typename... Strings>
26ResultString StrCat(const Strings&... strings) {
27 return [](auto... string_views) {
28 std::size_t result_size = 0;
29 ((result_size += string_views.size()), ...);
30
31 ResultString result;
32 result.reserve(result_size);
33 (result.append(string_views), ...);
34 return result;
35 }(std::string_view{strings}...);
36}
37
38namespace impl {
39
40template <typename Container>
41concept HasMappedType = requires { typename Container::mapped_type; };
42
43} // namespace impl
44
45/// @brief Returns nullptr if no key in associative container, otherwise
46/// returns pointer to value.
47template <typename Container, typename Key>
48auto* FindOrNullptr(Container& container, const Key& key) {
49 const auto it = container.find(key);
50 if constexpr (impl::HasMappedType<Container>) {
51 return (it != container.end() ? std::addressof(it->second) : nullptr);
52 } else {
53 return (it != container.end() ? std::addressof(*it) : nullptr);
54 }
55}
56
57/// @brief Returns default value if no key in associative container, otherwise
58/// returns a copy of the stored value.
59template <typename Container, typename Key, typename Default>
60auto FindOrDefault(Container& container, const Key& key, Default&& def) {
61 const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key);
62 return (ptr ? *ptr : decltype(*ptr){std::forward<Default>(def)});
63}
64
65/// @brief Returns default value if no key in associative container, otherwise
66/// returns a copy of the stored value.
67template <typename Container, typename Key>
68auto FindOrDefault(Container& container, const Key& key) {
69 const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key);
70 return (ptr ? *ptr : decltype(*ptr){});
71}
72
73/// @brief Returns std::nullopt if no key in associative container, otherwise
74/// returns std::optional with a copy of value
75template <typename Container, typename Key>
76auto FindOptional(Container& container, const Key& key) {
77 const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key);
78 return (ptr ? std::make_optional(*ptr) : std::nullopt);
79}
80
81/// @brief Searches a map for an element and return a checked pointer to
82/// the found element
83template <typename Container, typename Key>
84auto CheckedFind(Container& container, const Key& key) {
85 return utils::MakeCheckedPtr(USERVER_NAMESPACE::utils::FindOrNullptr(container, key));
86}
87
88/// @brief Converts one container type to another
89template <typename ToContainer, typename FromContainer>
90ToContainer AsContainer(FromContainer&& container) {
91 if constexpr (std::is_rvalue_reference_v<decltype(container)>) {
92 return ToContainer(
93 std::make_move_iterator(std::begin(container)),
94 std::make_move_iterator(std::end(container))
95 );
96 } else {
97 return ToContainer(std::begin(container), std::end(container));
98 }
99}
100
101namespace impl {
102
103template <typename ToContainer, typename Range>
104ToContainer AsContainerViaInsert(Range&& range) {
105 ToContainer result;
106 if constexpr (requires { result.reserve(std::ranges::size(range)); }) {
107 result.reserve(std::ranges::size(range));
108 }
109 for (auto&& ref : range) {
110 result.insert(result.end(), std::forward<decltype(ref)>(ref));
111 }
112 return result;
113}
114
115template <typename Container>
116concept HasKeyType = requires { typename Container::key_type; };
117
118} // namespace impl
119
120/// @brief Erased elements and returns number of deleted elements
121template <typename Container, typename Pred>
122auto EraseIf(Container& container, Pred pred) {
123 if constexpr (impl::HasKeyType<Container>) {
124 auto old_size = container.size();
125 for (auto it = std::begin(container), last = std::end(container); it != last;) {
126 if (pred(*it)) {
127 it = container.erase(it);
128 } else {
129 ++it;
130 }
131 }
132 return old_size - container.size();
133 } else {
134 auto it = std::remove_if(std::begin(container), std::end(container), pred);
135 const auto removed = std::distance(it, std::end(container));
136 container.erase(it, std::end(container));
137 return removed;
138 }
139}
140
141/// @brief Erased elements and returns number of deleted elements
142template <typename Container, typename T>
143size_t Erase(Container& container, const T& elem) {
144 if constexpr (impl::HasKeyType<Container>) {
145 return container.erase(elem);
146 } else {
147 // NOLINTNEXTLINE(readability-qualified-auto)
148 auto it = std::remove(std::begin(container), std::end(container), elem);
149 const auto removed = std::distance(it, std::end(container));
150 container.erase(it, std::end(container));
151 return removed;
152 }
153}
154
155/// @brief returns true if there is an element in the container which satisfies
156/// the predicate
157template <typename Container, typename Pred>
158bool ContainsIf(const Container& container, Pred pred) {
159 return std::ranges::find_if(container, pred) != std::end(container);
160}
161
162/// @brief returns true if there is a specified element in the container
163template <typename Container>
164bool Contains(const Container& container, const typename Container::value_type& item) {
165 return std::ranges::find(container, item) != std::end(container);
166}
167
168} // namespace utils
169
170USERVER_NAMESPACE_END