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
36namespace impl {
37template <typename Container, typename = void>
38struct HasMappedType : std::false_type {};
39
40template <typename Container>
41struct HasMappedType<Container, std::void_t<typename Container::mapped_type>> : std::true_type {};
42
43template <typename Container>
44inline constexpr bool kHasMappedType = HasMappedType<Container>::value;
45} // namespace impl
46
47/// @brief Returns nullptr if no key in associative container, otherwise
48/// returns pointer to value.
49template <typename Container, typename Key>
50auto* FindOrNullptr(Container& container, const Key& key) {
51 const auto it = container.find(key);
52 if constexpr (impl::kHasMappedType<Container>) {
53 return (it != container.end() ? &(it->second) : nullptr);
54 } else {
55 return (it != container.end() ? &(*it) : nullptr);
56 }
57}
58
59/// @brief Returns default value if no key in associative container, otherwise
60/// returns a copy of the stored value.
61template <typename Container, typename Key, typename Default>
62auto FindOrDefault(Container& container, const Key& key, Default&& def) {
63 const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key);
64 return (ptr ? *ptr : decltype(*ptr){std::forward<Default>(def)});
65}
66
67/// @brief Returns default value if no key in associative container, otherwise
68/// returns a copy of the stored value.
69template <typename Container, typename Key>
70auto FindOrDefault(Container& container, const Key& key) {
71 const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key);
72 return (ptr ? *ptr : decltype(*ptr){});
73}
74
75/// @brief Returns std::nullopt if no key in associative container, otherwise
76/// returns std::optional with a copy of value
77template <typename Container, typename Key>
78auto FindOptional(Container& container, const Key& key) {
79 const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key);
80 return (ptr ? std::make_optional(*ptr) : std::nullopt);
81}
82
83/// @brief Searches a map for an element and return a checked pointer to
84/// the found element
85template <typename Container, typename Key>
86auto CheckedFind(Container& container, const Key& key) {
87 return utils::MakeCheckedPtr(USERVER_NAMESPACE::utils::FindOrNullptr(container, key));
88}
89
90/// @brief Converts one container type to another
91template <typename ToContainer, typename FromContainer>
92ToContainer AsContainer(FromContainer&& container) {
93 if constexpr (std::is_rvalue_reference_v<decltype(container)>) {
94 return ToContainer(
95 std::make_move_iterator(std::begin(container)),
96 std::make_move_iterator(std::end(container))
97 );
98 } else {
99 return ToContainer(std::begin(container), std::end(container));
100 }
101}
102
103namespace impl {
104template <typename Container, typename = void>
105struct HasKeyType : std::false_type {};
106
107template <typename Container>
108struct HasKeyType<Container, std::void_t<typename Container::key_type>> : std::true_type {};
109
110template <typename Container>
111inline constexpr bool kHasKeyType = HasKeyType<Container>::value;
112} // namespace impl
113
114/// @brief Erased elements and returns number of deleted elements
115template <typename Container, typename Pred>
116auto EraseIf(Container& container, Pred pred) {
117 if constexpr (impl::kHasKeyType<Container>) {
118 auto old_size = container.size();
119 for (auto it = std::begin(container), last = std::end(container); 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 <typename Container, typename T>
137size_t Erase(Container& container, const T& elem) {
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 the 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) != std::end(container);
154}
155
156/// @brief returns true if there is a specified element in the container
157template <typename Container>
158bool Contains(const Container& container, const typename Container::value_type& item) {
159 return std::find(std::begin(container), std::end(container), item) != std::end(container);
160}
161
162} // namespace utils
163
164USERVER_NAMESPACE_END