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
return
(ptr ? *ptr :
decltype
(*ptr){std::forward<Default>(def)});
64
}
65
66
/// @brief Returns default value if no key in associative container, otherwise
67
/// returns a copy of the stored value.
68
template
<
typename
Container,
typename
Key>
69
auto
FindOrDefault
(Container& container,
const
Key& key) {
70
const
auto
* ptr = USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key);
71
return
(ptr ? *ptr :
decltype
(*ptr){});
72
}
73
74
/// @brief Returns std::nullopt if no key in associative container, otherwise
75
/// returns std::optional with a copy of value
76
template
<
typename
Container,
typename
Key>
77
auto
FindOptional
(Container& container,
const
Key& key) {
78
const
auto
* ptr = USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key);
79
return
(ptr ? std::make_optional(*ptr) : std::nullopt);
80
}
81
82
/// @brief Searches a map for an element and return a checked pointer to
83
/// the found element
84
template
<
typename
Container,
typename
Key>
85
auto
CheckedFind
(Container& container,
const
Key& key) {
86
return
utils
::MakeCheckedPtr(USERVER_NAMESPACE::
utils
::FindOrNullptr(container, key));
87
}
88
89
/// @brief Converts one container type to another
90
///
91
/// @warning This function moves from elements if the range is an rvalue. This is not correct for rvalue ranges that
92
/// do not own their elements, such as typical views. For example:
93
/// @code
94
/// std::vector<std::string> v = {"hello", "world"};
95
/// auto result = utils::AsContainer<std::vector<std::string>>(
96
/// v | std::views::filter([](const auto& item) { return true; })
97
/// );
98
/// @endcode
99
/// This will move the items from `v` to `result`, which is not what you want if you want to keep the original vector.
100
///
101
/// Use C++23 `std::ranges::to` instead if possible, optionally paired with `std::ranges::as_rvalue`.
102
template
<
typename
ToContainer,
typename
FromContainer>
103
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
104
ToContainer
AsContainer
(FromContainer&& container) {
105
if
constexpr
(std::is_rvalue_reference_v<
decltype
(container)>) {
106
return
ToContainer(
107
std::make_move_iterator(std::ranges::begin(container)),
108
std::make_move_iterator(std::ranges::end(container))
109
);
110
}
else
{
111
return
ToContainer(std::ranges::begin(container), std::ranges::end(container));
112
}
113
}
114
115
namespace
impl {
116
117
template
<
typename
ToContainer,
typename
Range>
118
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
119
ToContainer AsContainerViaInsert(Range&& range) {
120
ToContainer result;
121
if
constexpr
(
requires
{ result.reserve(std::ranges::size(range)); }) {
122
result.reserve(std::ranges::size(range));
123
}
124
for
(
auto
&& ref : range) {
125
result.insert(std::ranges::end(result), std::forward<
decltype
(ref)>(ref));
126
}
127
return
result;
128
}
129
130
template
<
typename
Container>
131
concept
HasKeyType =
requires
{
typename
Container::key_type; };
132
133
}
// namespace impl
134
135
/// @brief Erased elements and returns number of deleted elements
136
template
<
typename
Container,
typename
Pred>
137
std
::
integral
auto
EraseIf
(Container& container, Pred pred) {
138
if
constexpr
(impl::HasKeyType<Container>) {
139
auto
old_size = std::ranges::size(container);
140
for
(
auto
it = std::ranges::begin(container), last = std::ranges::end(container); it != last;) {
141
if
(pred(*it)) {
142
it = container.erase(it);
143
}
else
{
144
++it;
145
}
146
}
147
return
old_size - std::ranges::size(container);
148
}
else
{
149
auto
garbage = std::ranges::remove_if(container, pred);
150
container.erase(garbage.begin(), garbage.end());
151
return
std::ranges::size(garbage);
152
}
153
}
154
155
/// @brief Erased elements and returns number of deleted elements
156
template
<
typename
Container,
typename
T>
157
std
::
integral
auto
Erase
(Container& container,
const
T& elem) {
158
if
constexpr
(impl::HasKeyType<Container>) {
159
return
container.erase(elem);
160
}
else
{
161
// NOLINTNEXTLINE(readability-qualified-auto)
162
auto
garbage = std::ranges::remove(container, elem);
163
container.erase(garbage.begin(), garbage.end());
164
return
std::ranges::size(garbage);
165
}
166
}
167
168
/// @brief returns true if there is an element in the container which satisfies the predicate.
169
///
170
/// @deprecated Use `std::ranges::any_of` instead.
171
template
<
typename
Container,
typename
Pred>
172
bool
ContainsIf
(
const
Container& container, Pred pred) {
173
return
std::ranges::any_of(container, pred);
174
}
175
176
/// @brief returns true if there is a specified element in the container
177
///
178
/// In C++23, use `std::ranges::contains` instead.
179
template
<
typename
Container,
typename
Item>
180
bool
Contains
(
const
Container& container,
const
Item& item) {
181
return
std::ranges::find(container, item) != std::ranges::end(container);
182
}
183
184
}
// namespace utils
185
186
USERVER_NAMESPACE_END
userver
utils
algo.hpp
Generated on
for userver by
Doxygen
1.17.0