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 <map>
16#include <memory>
17#include <optional>
18#include <set>
19#include <type_traits>
20#include <typeinfo>
21#include <unordered_map>
22#include <unordered_set>
23#include <utility>
24#include <variant>
25#include <vector>
26
27#include <userver/utils/constexpr_indices.hpp>
28#include <userver/utils/lazy_prvalue.hpp>
29#include <userver/utils/meta.hpp>
30#include <userver/utils/strong_typedef.hpp>
31
32#include <userver/dump/common.hpp>
33#include <userver/dump/meta.hpp>
34#include <userver/dump/meta_containers.hpp>
35#include <userver/dump/operations.hpp>
36
37/// @cond
38namespace boost {
39
40namespace bimaps {
41
42template <typename L, typename R, typename AP1, typename AP2, typename AP3>
43class bimap;
44
45} // namespace bimaps
46
47namespace multi_index {
48
49template <typename Value, typename IndexSpecifierList, typename Allocator>
50class multi_index_container;
51
52} // namespace multi_index
53
54using bimaps::bimap;
55using multi_index::multi_index_container;
56
57} // namespace boost
58/// @endcond
59
60USERVER_NAMESPACE_BEGIN
61
62namespace dump {
63
64namespace impl {
65
66template <typename L, typename R, typename... Args>
67using BoostBimap = boost::bimap<L, R, Args...>;
68
69template <typename L, typename R, typename... Args>
70using BoostBimapLeftKey = typename BoostBimap<L, R, Args...>::left_key_type;
71
72template <typename L, typename R, typename... Args>
73using BoostBimapRightKey = typename BoostBimap<L, R, Args...>::right_key_type;
74
75template <typename T>
76auto ReadLazyPrvalue(Reader& reader) {
77 return utils::LazyPrvalue([&reader] { return reader.Read<T>(); });
78}
79
80[[noreturn]] void ThrowInvalidVariantIndex(const std::type_info& type,
81 std::size_t index);
82
83template <typename VariantType>
84VariantType ReadVariant(Reader& reader, std::size_t index) {
85 static constexpr auto VariantSize = std::variant_size_v<VariantType>;
86 std::optional<VariantType> result;
87
88 utils::WithConstexprIndex<VariantSize>(index, [&](auto index_constant) {
89 static constexpr auto kIndex = decltype(index_constant)::value;
90 using Alternative = std::variant_alternative_t<kIndex, VariantType>;
91 // Not using ReadLazyPrvalue because of stdlib issues on some compilers.
92 result.emplace(std::in_place_index<kIndex>, reader.Read<Alternative>());
93 });
94
95 return std::move(*result);
96}
97
98} // namespace impl
99
100/// @brief Container serialization support
101template <typename T>
103 Writer& writer, const T& value) {
105 for (const auto& item : value) {
106 // explicit cast for vector<bool> shenanigans
107 writer.Write(static_cast<const meta::RangeValueType<T>&>(item));
108 }
109}
110
111/// @brief Container deserialization support
112template <typename T>
115 const auto size = reader.Read<std::size_t>();
116 T result{};
117 if constexpr (meta::kIsReservable<T>) {
119 }
120 for (std::size_t i = 0; i < size; ++i) {
122 }
123 return result;
124}
125
126/// @brief Pair serialization support (for maps)
127template <typename T, typename U>
129 Writer& writer, const std::pair<T, U>& value) {
132}
133
134/// @brief Pair deserialization support (for maps)
135template <typename T, typename U>
137 Reader& reader, To<std::pair<T, U>>) {
138 return {reader.Read<T>(), reader.Read<U>()};
139}
140
141/// @brief `std::optional` serialization support
142template <typename T>
144 const std::optional<T>& value) {
146 if (value) writer.Write(*value);
147}
148
149/// @brief `std::optional` deserialization support
150template <typename T>
152 To<std::optional<T>>) {
153 if (!reader.Read<bool>()) return std::nullopt;
154 return impl::ReadLazyPrvalue<T>(reader);
155}
156
157/// @brief `std::variant` serialization support
158template <typename... Args>
160 Writer& writer, const std::variant<Args...>& value) {
162 std::visit([&writer](const auto& inner) { writer.Write(inner); }, value);
163}
164
165/// @brief `std::variant` deserialization support
166template <typename... Args>
167std::enable_if_t<(true && ... &&
169 std::variant<Args...>>
171 const auto index = reader.Read<std::size_t>();
172 if (index >= sizeof...(Args)) {
174 }
175 return impl::ReadVariant<std::variant<Args...>>(reader, index);
176}
177
178/// Allows reading `const T`, which is usually encountered as a member of some
179/// container
180template <typename T>
182 return Read(reader, To<T>{});
183}
184
185/// @brief utils::StrongTypedef serialization support
186template <typename Tag, typename T, utils::StrongTypedefOps Ops>
190}
191
192/// @brief utils::StrongTypedef deserialization support
193template <typename Tag, typename T, utils::StrongTypedefOps Ops>
196 return utils::StrongTypedef<Tag, T, Ops>{reader.Read<T>()};
197}
198
199/// @brief `std::unique_ptr` serialization support
200template <typename T>
202 const std::unique_ptr<T>& ptr) {
203 writer.Write(static_cast<bool>(ptr));
204 if (ptr) writer.Write(*ptr);
205}
206
207/// @brief `std::unique_ptr` deserialization support
208template <typename T>
211 if (!reader.Read<bool>()) return {};
213}
214
215/// @brief `std::shared_ptr` serialization support
216/// @warning If two or more `shared_ptr` within a single dumped entity point to
217/// the same object, they will point to its distinct copies after loading a dump
218template <typename T>
220 const std::shared_ptr<T>& ptr) {
221 writer.Write(static_cast<bool>(ptr));
222 if (ptr) writer.Write(*ptr);
223}
224
225/// @brief `std::shared_ptr` deserialization support
226/// @warning If two or more `shared_ptr` within a single dumped entity point to
227/// the same object, they will point to its distinct copies after loading a dump
228template <typename T>
231 if (!reader.Read<bool>()) return {};
233}
234
235/// @brief `boost::bimap` serialization support
236template <typename L, typename R, typename... Args>
239Write(Writer& writer, const boost::bimap<L, R, Args...>& map) {
240 writer.Write(map.size());
241
242 for (const auto& [left, right] : map) {
245 }
246}
247
248/// @brief `boost::bimap` deserialization support
249template <typename L, typename R, typename... Args>
252 boost::bimap<L, R, Args...>>
254 using BoostBimap = impl::BoostBimap<L, R, Args...>;
255
258
259 using BoostBimapSizeType = typename BoostBimap::size_type;
260
262 // bimap doesn't have reserve :(
263
264 const auto size = reader.Read<BoostBimapSizeType>();
265 for (BoostBimapSizeType i = 0; i < size; ++i) {
266 // `Read`s are guaranteed to occur left-to-right in brace-init
267 map.insert({
270 });
271 }
272
273 return map;
274}
275
276/// @brief `boost::multi_index_container` serialization support
277template <typename T, typename Index, typename Alloc>
279 Writer& writer,
281 writer.Write(container.template get<0>().size());
282 for (auto& item : container.template get<0>()) {
284 }
285}
286
287/// @brief `boost::multi_index_container` deserialization support
288template <typename T, typename Index, typename Alloc>
291 const auto size = reader.Read<std::size_t>();
293
294 // boost::multi_index_container has reserve() with some, but not all, configs
295 if constexpr (meta::kIsReservable<
298 }
299
300 for (std::size_t i = 0; i < size; ++i) {
302 }
303
304 return container;
305}
306
307} // namespace dump
308
309USERVER_NAMESPACE_END