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
36/// @brief Returns nullptr if no key in associative container, otherwise
37/// returns pointer to value.
38template <class Map, class Key>
39auto* FindOrNullptr(Map& map, const Key& key) {
40 const auto it = map.find(key);
41 return (it == map.end() ? nullptr : &it->second);
42}
43
44/// @brief Returns default value if no key in associative container, otherwise
45/// returns a copy of the stored value.
46template <class Map, class Key, class Default>
47typename Map::mapped_type FindOrDefault(Map& map, const Key& key, Default&& def) {
48 const auto* ptr = utils::FindOrNullptr(map, key);
49 if (!ptr) {
50 return {std::forward<Default>(def)};
51 }
52 return *ptr;
53}
54
55/// @brief Returns default value if no key in associative container, otherwise
56/// returns a copy of the stored value.
57template <class Map, class Key>
58typename Map::mapped_type FindOrDefault(Map& map, const Key& key) {
59 const auto ptr = USERVER_NAMESPACE::utils::FindOrNullptr(map, key);
60 if (!ptr) {
61 return {};
62 }
63 return *ptr;
64}
65
66/// @brief Returns std::nullopt if no key in associative container, otherwise
67/// returns std::optional with a copy of value
68template <class Map, class Key>
69std::optional<typename Map::mapped_type> FindOptional(Map& map, const Key& key) {
70 const auto ptr = utils::FindOrNullptr(map, key);
71 if (!ptr) {
72 return std::nullopt;
73 }
74 return {*ptr};
75}
76
77/// @brief Searches a map for an element and return a checked pointer to
78/// the found element
79template <typename Map, typename Key>
80auto CheckedFind(Map& map, const Key& key) -> decltype(utils::MakeCheckedPtr(&map.find(key)->second)) {
81 if (auto f = map.find(key); f != map.end()) {
82 return utils::MakeCheckedPtr(&f->second);
83 }
84 return {nullptr};
85}
86
87/// @brief Converts one container type to another
88template <class ToContainer, class 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)), std::make_move_iterator(std::end(container))
93 );
94 } else {
95 return ToContainer(std::begin(container), std::end(container));
96 }
97}
98
99namespace impl {
100template <typename Container, typename = void>
101struct HasKeyType : std::false_type {};
102
103template <typename Container>
104struct HasKeyType<Container, std::void_t<typename Container::key_type>> : std::true_type {};
105
106template <typename Container>
107inline constexpr bool kHasKeyType = HasKeyType<Container>::value;
108} // namespace impl
109
110/// @brief Erased elements and returns number of deleted elements
111template <class Container, class Pred>
112auto EraseIf(Container& container, Pred pred) {
113 if constexpr (impl::kHasKeyType<Container>) {
114 auto old_size = container.size();
115 for (auto it = std::begin(container), last = std::end(container); it != last;) {
116 if (pred(*it)) {
117 it = container.erase(it);
118 } else {
119 ++it;
120 }
121 }
122 return old_size - container.size();
123 } else {
124 auto it = std::remove_if(std::begin(container), std::end(container), pred);
125 const auto removed = std::distance(it, std::end(container));
126 container.erase(it, std::end(container));
127 return removed;
128 }
129}
130
131/// @brief Erased elements and returns number of deleted elements
132template <class Container, class T>
134 if constexpr (impl::kHasKeyType<Container>) {
135 return container.erase(elem);
136 } else {
137 // NOLINTNEXTLINE(readability-qualified-auto)
138 auto it = std::remove(std::begin(container), std::end(container), elem);
139 const auto removed = std::distance(it, std::end(container));
140 container.erase(it, std::end(container));
141 return removed;
142 }
143}
144
145/// @brief returns true if there is an element in container which satisfies
146/// the predicate
147template <typename Container, typename Pred>
148bool ContainsIf(const Container& container, Pred pred) {
149 return std::find_if(std::begin(container), std::end(container), pred) != std::end(container);
150}
151
152} // namespace utils
153
154USERVER_NAMESPACE_END