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