userver: userver/utils/box.hpp Source File
Loading...
Searching...
No Matches
box.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/box.hpp
4/// @brief @copybrief utils::Box
5
6#include <memory>
7#include <type_traits>
8#include <utility>
9
10#include <userver/formats/parse/to.hpp>
11#include <userver/formats/serialize/to.hpp>
12#include <userver/logging/log_helper_fwd.hpp>
13#include <userver/utils/assert.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace utils {
18
19namespace impl {
20
21template <typename Self, typename... Args>
22inline constexpr bool kArgsAreNotSelf =
23 ((sizeof...(Args) > 1) || ... || !std::is_same_v<std::decay_t<Args>, Self>);
24
25template <bool Condition, template <typename...> typename Trait,
26 typename... Args>
27constexpr bool ConjunctionWithTrait() noexcept {
28 if constexpr (Condition) {
29 return Trait<Args...>::value;
30 } else {
31 return false;
32 }
33}
34
35} // namespace impl
36
37/// @brief Remote storage for a single item. Implemented as a unique pointer
38/// that is never `null`, except when moved from.
39///
40/// Has the semantics of non-optional `T`.
41/// Copies the content on copy, compares by the contained value.
42///
43/// Use in the following cases:
44/// - to create recursive types while maintaining value semantics;
45/// - to hide the implementation of a class in cpp;
46/// - to prevent the large size or alignment of a field from inflating the size
47/// or alignment of an object.
48///
49/// Use utils::UniqueRef instead:
50/// - to add a non-movable field to a movable object;
51/// - to own an object of a polymorphic base class.
52///
53/// Usage example:
54/// @snippet utils/box_test.cpp sample
55template <typename T>
56class Box {
57 public:
58 /// Allocate a default-constructed value.
59 // Would like to use SFINAE here, but std::optional<Box> requests tests for
60 // default construction eagerly, which errors out for a forward-declared T.
62
63 /// Allocate a `T`, copying or moving @a arg.
64 template <typename U = T,
65 std::enable_if_t<impl::ConjunctionWithTrait<
66 // Protection against hiding special
67 // constructors.
68 impl::kArgsAreNotSelf<Box, U>,
69 // Only allow the implicit conversion to Box<T>
70 // if U is implicitly convertible to T. Also,
71 // support SFINAE.
72 std::is_convertible, U&&, T>(),
73 int> = 0>
74 /*implicit*/ Box(U&& arg)
75 : data_(std::make_unique<T>(std::forward<U>(arg))) {}
76
77 /// Allocate the value, emplacing it with the given @a args.
78 template <typename... Args,
79 std::enable_if_t<impl::ConjunctionWithTrait<
80 // Protection against hiding special
81 // constructors.
82 impl::kArgsAreNotSelf<Box, Args...>,
83 // Support SFINAE.
84 std::is_constructible, T, Args&&...>(),
85 int> = 0>
86 explicit Box(Args&&... args)
87 : data_(std::make_unique<T>(std::forward<Args>(args)...)) {}
88
89 /// Allocate the value as constructed by the given @a factory.
90 /// Allows to save an extra move of the contained value.
91 template <typename Factory>
92 static Box MakeWithFactory(Factory&& factory) {
93 return Box(EmplaceFactory{}, std::forward<Factory>(factory));
94 }
95
96 Box(Box&& other) noexcept = default;
97 Box& operator=(Box&& other) noexcept = default;
98
99 Box(const Box& other) : data_(std::make_unique<T>(*other)) {}
100
101 Box& operator=(const Box& other) {
102 *this = Box{other};
103 return *this;
104 }
105
106 /// Assigns-through to the contained value.
107 template <typename U = T,
108 std::enable_if_t<impl::ConjunctionWithTrait< //
109 impl::ConjunctionWithTrait<
110 // Protection against hiding
111 // special constructors.
112 impl::kArgsAreNotSelf<Box, U>,
113 // Support SFINAE.
114 std::is_constructible, T, U>(),
115 std::is_assignable, T&, U>(),
116 int> = 0>
117 Box& operator=(U&& other) {
118 if (data_) {
119 *data_ = std::forward<U>(other);
120 } else {
121 data_ = std::make_unique<T>(std::forward<U>(other));
122 }
123 return *this;
124 }
125
126 // Box is always engaged, unless moved-from. Just call *box.
127 /*implicit*/ operator bool() const = delete;
128
129 T* operator->() noexcept { return Get(); }
130 const T* operator->() const noexcept { return Get(); }
131
132 T& operator*() noexcept { return *Get(); }
133 const T& operator*() const noexcept { return *Get(); }
134
135 bool operator==(const Box& other) const { return **this == *other; }
136
137 bool operator!=(const Box& other) const { return **this != *other; }
138
139 bool operator<(const Box& other) const { return **this < *other; }
140
141 bool operator>(const Box& other) const { return **this > *other; }
142
143 bool operator<=(const Box& other) const { return **this <= *other; }
144
145 bool operator>=(const Box& other) const { return **this >= *other; }
146
147 private:
148 struct EmplaceFactory final {};
149
150 template <typename Factory>
151 explicit Box(EmplaceFactory, Factory&& factory)
152 : data_(new T(std::forward<Factory>(factory)())) {}
153
154 T* Get() noexcept {
155 UASSERT_MSG(data_, "Accessing a moved-from Box");
156 return data_.get();
157 }
158
159 const T* Get() const noexcept {
160 UASSERT_MSG(data_, "Accessing a moved-from Box");
161 return data_.get();
162 }
163
164 std::unique_ptr<T> data_;
165};
166
167template <typename Value, typename T>
168Box<T> Parse(const Value& value, formats::parse::To<Box<T>>) {
169 return Box<T>::MakeWithFactory([&value] { return value.template As<T>(); });
170}
171
172template <typename Value, typename T>
173Value Serialize(const Box<T>& value, formats::serialize::To<Value>) {
174 return Serialize(*value, formats::serialize::To<Value>{});
175}
176
177template <typename StringBuilder, typename T>
178void WriteToStream(const Box<T>& value, StringBuilder& sw) {
179 WriteToStream(*value, sw);
180}
181
182template <typename T>
183logging::LogHelper& operator<<(logging::LogHelper& lh, const Box<T>& box) {
184 lh << *box;
185 return lh;
186}
187
188} // namespace utils
189
190USERVER_NAMESPACE_END