userver: userver/utils/algo.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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)), std::make_move_iterator(std::end(container))
96 );
97 } else {
98 return ToContainer(std::begin(container), std::end(container));
99 }
100}
101
102namespace impl {
103template <typename Container, typename = void>
104struct HasKeyType : std::false_type {};
105
106template <typename Container>
107struct HasKeyType<Container, std::void_t<typename Container::key_type>> : 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 <typename Container, typename 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); it != last;) {
119 if (pred(*it)) {
120 it = container.erase(it);
121 } else {
122 ++it;
123 }
124 }
125 return old_size - container.size();
126 } else {
127 auto it = std::remove_if(std::begin(container), std::end(container), pred);
128 const auto removed = std::distance(it, std::end(container));
129 container.erase(it, std::end(container));
130 return removed;
131 }
132}
133
134/// @brief Erased elements and returns number of deleted elements
135template <typename Container, typename T>
136size_t Erase(Container& container, const T& elem) {
137 if constexpr (impl::kHasKeyType<Container>) {
138 return container.erase(elem);
139 } else {
140 // NOLINTNEXTLINE(readability-qualified-auto)
141 auto it = std::remove(std::begin(container), std::end(container), elem);
142 const auto removed = std::distance(it, std::end(container));
143 container.erase(it, std::end(container));
144 return removed;
145 }
146}
147
148/// @brief returns true if there is an element in container which satisfies
149/// the predicate
150template <typename Container, typename Pred>
151bool ContainsIf(const Container& container, Pred pred) {
152 return std::find_if(std::begin(container), std::end(container), pred) != std::end(container);
153}
154
155} // namespace utils
156
157USERVER_NAMESPACE_END