userver: userver/dump/common_containers.hpp Source File
Loading...
Searching...
No Matches
common_containers.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/dump/common_containers.hpp
4/// @brief Dump support for C++ Standard Library and Boost containers,
5/// `std::optional`, utils::StrongTypedef, `std::{unique,shared}_ptr`
6///
7/// @note There are no traits in `CachingComponentBase`. If `T`
8/// is writable/readable, we have to generate the code for dumps
9/// regardless of `dump: enabled`. So it's important that all Read-Write
10/// operations for containers are SFINAE-correct.
11///
12/// @ingroup userver_dump_read_write
13
14#include <cstddef>
15#include <memory>
16#include <optional>
17#include <type_traits>
18#include <typeinfo>
19#include <utility>
20#include <variant>
21
22#include <userver/utils/constexpr_indices.hpp>
23#include <userver/utils/lazy_prvalue.hpp>
24#include <userver/utils/meta.hpp>
25#include <userver/utils/strong_typedef.hpp>
26
27#include <userver/dump/common.hpp>
28#include <userver/dump/meta.hpp>
29#include <userver/dump/meta_containers.hpp>
30#include <userver/dump/operations.hpp>
31
32/// @cond
33namespace boost {
34
35namespace bimaps {
36
37template <typename L, typename R, typename AP1, typename AP2, typename AP3>
38class bimap;
39
40} // namespace bimaps
41
42namespace multi_index {
43
44template <typename Value, typename IndexSpecifierList, typename Allocator>
45class multi_index_container;
46
47} // namespace multi_index
48
49using bimaps::bimap;
50using multi_index::multi_index_container;
51
52} // namespace boost
53/// @endcond
54
55USERVER_NAMESPACE_BEGIN
56
57namespace dump {
58
59namespace impl {
60
61template <typename L, typename R, typename... Args>
62using BoostBimap = boost::bimap<L, R, Args...>;
63
64template <typename L, typename R, typename... Args>
65using BoostBimapLeftKey = typename BoostBimap<L, R, Args...>::left_key_type;
66
67template <typename L, typename R, typename... Args>
68using BoostBimapRightKey = typename BoostBimap<L, R, Args...>::right_key_type;
69
70template <typename T>
71auto ReadLazyPrvalue(Reader& reader) {
72 return utils::LazyPrvalue([&reader] { return reader.Read<T>(); });
73}
74
75[[noreturn]] void ThrowInvalidVariantIndex(const std::type_info& type, std::size_t index);
76
77template <typename VariantType>
78VariantType ReadVariant(Reader& reader, std::size_t index) {
79 static constexpr auto VariantSize = std::variant_size_v<VariantType>;
80 std::optional<VariantType> result;
81
82 utils::WithConstexprIndex<VariantSize>(index, [&](auto index_constant) {
83 static constexpr auto kIndex = decltype(index_constant)::value;
84 using Alternative = std::variant_alternative_t<kIndex, VariantType>;
85 // Not using ReadLazyPrvalue because of stdlib issues on some compilers.
86 result.emplace(std::in_place_index<kIndex>, reader.Read<Alternative>());
87 });
88
89 return std::move(*result);
90}
91
92} // namespace impl
93
94/// @brief Container serialization support
95template <typename T>
98 for (const auto& item : value) {
99 // explicit cast for vector<bool> shenanigans
100 writer.Write(static_cast<const meta::RangeValueType<T>&>(item));
101 }
102}
103
104/// @brief Container deserialization support
105template <typename T>
107 const auto size = reader.Read<std::size_t>();
108 T result{};
109 if constexpr (meta::kIsReservable<T>) {
111 }
112 for (std::size_t i = 0; i < size; ++i) {
114 }
115 return result;
116}
117
118/// @brief Pair serialization support (for maps)
119template <typename T, typename U>
123}
124
125/// @brief Pair deserialization support (for maps)
126template <typename T, typename U>
128 return {reader.Read<T>(), reader.Read<U>()};
129}
130
131/// @brief `std::optional` serialization support
132template <typename T>
135 if (value) writer.Write(*value);
136}
137
138/// @brief `std::optional` deserialization support
139template <typename T>
141 if (!reader.Read<bool>()) return std::nullopt;
142 return impl::ReadLazyPrvalue<T>(reader);
143}
144
145/// @brief `std::variant` serialization support
146template <typename... Args>
147std::enable_if_t<(true && ... && kIsWritable<Args>)> Write(Writer& writer, const std::variant<Args...>& value) {
149 std::visit([&writer](const auto& inner) { writer.Write(inner); }, value);
150}
151
152/// @brief `std::variant` deserialization support
153template <typename... Args>
156 const auto index = reader.Read<std::size_t>();
157 if (index >= sizeof...(Args)) {
159 }
160 return impl::ReadVariant<std::variant<Args...>>(reader, index);
161}
162
163/// Allows reading `const T`, which is usually encountered as a member of some
164/// container
165template <typename T>
167 return Read(reader, To<T>{});
168}
169
170/// @brief utils::StrongTypedef serialization support
171template <typename Tag, typename T, utils::StrongTypedefOps Ops>
174}
175
176/// @brief utils::StrongTypedef deserialization support
177template <typename Tag, typename T, utils::StrongTypedefOps Ops>
180 return utils::StrongTypedef<Tag, T, Ops>{reader.Read<T>()};
181}
182
183/// @brief `std::unique_ptr` serialization support
184template <typename T>
186 writer.Write(static_cast<bool>(ptr));
187 if (ptr) writer.Write(*ptr);
188}
189
190/// @brief `std::unique_ptr` deserialization support
191template <typename T>
193 if (!reader.Read<bool>()) return {};
195}
196
197/// @brief `std::shared_ptr` serialization support
198/// @warning If two or more `shared_ptr` within a single dumped entity point to
199/// the same object, they will point to its distinct copies after loading a dump
200template <typename T>
202 writer.Write(static_cast<bool>(ptr));
203 if (ptr) writer.Write(*ptr);
204}
205
206/// @brief `std::shared_ptr` deserialization support
207/// @warning If two or more `shared_ptr` within a single dumped entity point to
208/// the same object, they will point to its distinct copies after loading a dump
209template <typename T>
211 if (!reader.Read<bool>()) return {};
213}
214
215/// @brief `boost::bimap` serialization support
216template <typename L, typename R, typename... Args>
219Write(Writer& writer, const boost::bimap<L, R, Args...>& map) {
220 writer.Write(map.size());
221
222 for (const auto& [left, right] : map) {
225 }
226}
227
228/// @brief `boost::bimap` deserialization support
229template <typename L, typename R, typename... Args>
232 boost::bimap<L, R, Args...>>
234 using BoostBimap = impl::BoostBimap<L, R, Args...>;
235
238
239 using BoostBimapSizeType = typename BoostBimap::size_type;
240
242 // bimap doesn't have reserve :(
243
244 const auto size = reader.Read<BoostBimapSizeType>();
245 for (BoostBimapSizeType i = 0; i < size; ++i) {
246 // `Read`s are guaranteed to occur left-to-right in brace-init
247 map.insert({
250 });
251 }
252
253 return map;
254}
255
256/// @brief `boost::multi_index_container` serialization support
257template <typename T, typename Index, typename Alloc>
259 writer.Write(container.template get<0>().size());
260 for (auto& item : container.template get<0>()) {
262 }
263}
264
265/// @brief `boost::multi_index_container` deserialization support
266template <typename T, typename Index, typename Alloc>
269 const auto size = reader.Read<std::size_t>();
271
272 // boost::multi_index_container has reserve() with some, but not all, configs
275 }
276
277 for (std::size_t i = 0; i < size; ++i) {
279 }
280
281 return container;
282}
283
284} // namespace dump
285
286USERVER_NAMESPACE_END