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