userver
C++ Async Framework
Toggle main menu visibility
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
<
ranges
>
13
#
include
<
string
>
14
#
include
<
string_view
>
15
#
include
<
type_traits
>
16
#
include
<
utility
>
17
18
#
include
<
userver
/
utils
/
checked_pointer
.
hpp
>
19
20
USERVER_NAMESPACE_BEGIN
21
22
/// @brief General-purpose utilities used across userver libraries.
23
namespace
utils
{
24
25
/// @brief Concatenates multiple `std::string_view`-convertible items
26
template
<
typename
ResultString = std::string,
typename
... Strings>
27
ResultString
StrCat
(
const
Strings&... strings) {
28
return
[](
auto
... string_views) {
29
std::size_t result_size = 0;
30
((result_size += string_views.size()), ...);
31
32
ResultString result;
33
result.reserve(result_size);
34
(result.append(string_views), ...);
35
return
result;
36
}(std::string_view{strings}...);
37
}
38
39
namespace
impl {
40
41
template
<
typename
Container>
42
concept
HasMappedType =
requires
{
typename
Container::mapped_type; };
43
44
}
// namespace impl
45
46
/// @brief Returns nullptr if no key in associative container, otherwise
47
/// returns pointer to value.
48
template
<
typename
Container,
typename
Key>
49
auto
*
FindOrNullptr
(Container& container,
const
Key& key) {
50
const
auto
it = container.find(key);
51
if
constexpr
(impl::HasMappedType<Container>) {
52
return
(it != std::ranges::end(container) ? std::addressof(it->second) :
nullptr
);
53
}
else
{
54
return
(it != std::ranges::end(container) ? std::addressof(*it) :
nullptr
);
55
}
56
}
57
58
/// @brief Returns default value if no key in associative container, otherwise
59
/// returns a copy of the stored value.
60
template
<
typename
Container,
typename
Key,
typename
Default>
61
auto
FindOrDefault
(Container& container,
const
Key& key, Default&& def) {
62
const
auto
* ptr = USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key);
63
using
R = std::remove_cvref_t<
decltype
(*ptr)>;
64
return
(ptr ? *ptr : R(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.
69
template
<
typename
Container,
typename
Key>
70
auto
FindOrDefault
(Container& container,
const
Key& key) {
71
const
auto
* ptr = USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key);
72
using
R = std::remove_cvref_t<
decltype
(*ptr)>;
73
return
(ptr ? *ptr : R());
74
}
75
76
/// @brief Returns std::nullopt if no key in associative container, otherwise
77
/// returns std::optional with a copy of value
78
template
<
typename
Container,
typename
Key>
79
auto
FindOptional
(Container& container,
const
Key& key) {
80
const
auto
* ptr = USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key);
81
return
(ptr ? std::make_optional(*ptr) : std::nullopt);
82
}
83
84
/// @brief Searches a map for an element and return a checked pointer to
85
/// the found element
86
template
<
typename
Container,
typename
Key>
87
auto
CheckedFind
(Container& container,
const
Key& key) {
88
return
utils
::MakeCheckedPtr(USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key));
89
}
90
91
/// @brief Converts one container type to another
92
///
93
/// @warning This function moves from elements if the range is an rvalue. This is not correct for rvalue ranges that
94
/// do not own their elements, such as typical views. For example:
95
/// @code
96
/// std::vector<std::string> v = {"hello", "world"};
97
/// auto result = utils::AsContainer<std::vector<std::string>>(
98
/// v | std::views::filter([](const auto& item) { return true; })
99
/// );
100
/// @endcode
101
/// This will move the items from `v` to `result`, which is not what you want if you want to keep the original vector.
102
///
103
/// Use C++23 `std::ranges::to` instead if possible, optionally paired with `std::ranges::as_rvalue`.
104
template
<
typename
ToContainer,
typename
FromContainer>
105
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
106
ToContainer
AsContainer
(FromContainer&& container) {
107
if
constexpr
(std::is_rvalue_reference_v<
decltype
(container)>) {
108
return
ToContainer(
109
std::make_move_iterator(std::ranges::begin(container)),
110
std::make_move_iterator(std::ranges::end(container))
111
);
112
}
else
{
113
return
ToContainer(std::ranges::begin(container), std::ranges::end(container));
114
}
115
}
116
117
namespace
impl {
118
119
template
<
typename
ToContainer,
typename
Range>
120
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
121
ToContainer AsContainerViaInsert(Range&& range) {
122
ToContainer result;
123
if
constexpr
(
requires
{ result.reserve(std::ranges::size(range)); }) {
124
result.reserve(std::ranges::size(range));
125
}
126
for
(
auto
&& ref : range) {
127
result.insert(std::ranges::end(result), std::forward<
decltype
(ref)>(ref));
128
}
129
return
result;
130
}
131
132
template
<
typename
Container>
133
concept
HasKeyType =
requires
{
typename
Container::key_type; };
134
135
}
// namespace impl
136
137
/// @brief Erased elements and returns number of deleted elements
138
template
<
typename
Container,
typename
Pred>
139
std
::
integral
auto
EraseIf
(Container& container, Pred pred) {
140
if
constexpr
(impl::HasKeyType<Container>) {
141
auto
old_size = std::ranges::size(container);
142
for
(
auto
it = std::ranges::begin(container), last = std::ranges::end(container); it != last;) {
143
if
(pred(*it)) {
144
it = container.erase(it);
145
}
else
{
146
++it;
147
}
148
}
149
return
old_size - std::ranges::size(container);
150
}
else
{
151
auto
garbage = std::ranges::remove_if(container, pred);
152
container.erase(garbage.begin(), garbage.end());
153
return
std::ranges::size(garbage);
154
}
155
}
156
157
/// @brief Erased elements and returns number of deleted elements
158
template
<
typename
Container,
typename
T>
159
std
::
integral
auto
Erase
(Container& container,
const
T& elem) {
160
if
constexpr
(impl::HasKeyType<Container>) {
161
return
container.erase(elem);
162
}
else
{
163
// NOLINTNEXTLINE(readability-qualified-auto)
164
auto
garbage = std::ranges::remove(container, elem);
165
container.erase(garbage.begin(), garbage.end());
166
return
std::ranges::size(garbage);
167
}
168
}
169
170
/// @brief returns true if there is an element in the container which satisfies the predicate.
171
///
172
/// @deprecated Use `std::ranges::any_of` instead.
173
template
<
typename
Container,
typename
Pred>
174
bool
ContainsIf
(
const
Container& container, Pred pred) {
175
return
std::ranges::any_of(container, pred);
176
}
177
178
/// @brief returns true if there is a specified element in the container
179
///
180
/// In C++23, use `std::ranges::contains` instead.
181
template
<
typename
Container,
typename
Item>
182
bool
Contains
(
const
Container& container,
const
Item& item) {
183
return
std::ranges::find(container, item) != std::ranges::end(container);
184
}
185
186
}
// namespace utils
187
188
USERVER_NAMESPACE_END
userver
utils
algo.hpp
Generated on
for userver by
Doxygen
1.17.0