userver
C++ Async Framework
Toggle main menu visibility
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
<
concepts
>
7
#
include
<
memory
>
8
#
include
<
type_traits
>
9
#
include
<
utility
>
10
11
#
include
<
userver
/
formats
/
parse
/
to
.
hpp
>
12
#
include
<
userver
/
formats
/
serialize
/
to
.
hpp
>
13
#
include
<
userver
/
logging
/
log_helper_fwd
.
hpp
>
14
#
include
<
userver
/
utils
/
assert
.
hpp
>
15
16
USERVER_NAMESPACE_BEGIN
17
18
namespace
utils
{
19
20
template
<
typename
T>
21
class
Box
;
22
23
namespace
impl {
24
25
template
<
typename
T>
26
struct
IsBox : std::false_type {};
27
28
template
<
typename
... Args>
29
struct
IsBox<
Box
<Args...>> : std::true_type {};
30
31
}
// namespace impl
32
33
/// @brief Remote storage for a single item. Implemented as a unique pointer
34
/// that is never `null`, except when moved from.
35
///
36
/// Has the semantics of non-optional `T`.
37
/// Copies the content on copy, compares by the contained value.
38
///
39
/// Use in the following cases:
40
/// - to create recursive types while maintaining value semantics;
41
/// - to hide the implementation of a class in cpp;
42
/// - to prevent the large size or alignment of a field from inflating the size
43
/// or alignment of an object.
44
///
45
/// Use utils::UniqueRef instead:
46
/// - to add a non-movable field to a movable object;
47
/// - to own an object of a polymorphic base class.
48
///
49
/// Usage example:
50
/// @snippet universal/src/utils/box_test.cpp sample
51
template
<
typename
T>
52
class
Box
{
53
public
:
54
/// Allocate a default-constructed value.
55
// Would like to use SFINAE here, but std::optional<Box> requests tests for
56
// default construction eagerly, which errors out for a forward-declared T.
57
Box
()
58
: data_(std::make_unique<T>())
59
{}
60
61
/// Allocate a `T`, copying or moving @a arg.
62
///
63
/// `explicit` if and only if `T`'s constructor is `explicit`.
64
/// Split into `explicit` / `explicit(false)` overloads due to limitations of clang-16.
65
template
<
typename
U = T>
66
// Protect against hiding special constructors.
67
requires
(!std::same_as<std::remove_cvref_t<U>,
Box
>) &&
68
// Prevent infinite recursion from constructible_from in the next check.
69
(!impl::IsBox<std::remove_cvref_t<U>>::value) &&
70
// Prevent infinite recursion.
71
(std::same_as<std::remove_cvref_t<U>, T> || !std::is_constructible_v<T,
Box
>) &&
72
// Normal requirement.
73
std::is_convertible_v<U, T>
74
explicit
(
false
)
Box
(U&& arg)
75
: data_(std::make_unique<T>(std::forward<U>(arg)))
76
{}
77
78
/// @overload
79
template
<
typename
U = T>
80
// Protect against hiding special constructors.
81
requires
(!std::same_as<std::remove_cvref_t<U>,
Box
>) &&
82
// Prevent infinite recursion from constructible_from in the next check.
83
(!impl::IsBox<std::remove_cvref_t<U>>::value) &&
84
// Prevent infinite recursion.
85
(std::same_as<std::remove_cvref_t<U>, T> || !std::is_constructible_v<T,
Box
>) &&
86
// Normal requirement.
87
std::is_constructible_v<T, U> && (!std::is_convertible_v<U, T>)
88
explicit
Box
(U&& arg)
89
: data_(std::make_unique<T>(std::forward<U>(arg)))
90
{}
91
92
/// Allocate the value, emplacing it with the given @a args.
93
template
<
typename
... Args>
94
requires
(
sizeof
...(Args) >= 2) && std::is_constructible_v<T, Args...>
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
<
typename
U = T>
120
requires
(!std::same_as<std::remove_cvref_t<U>,
Box
>) && std::is_assignable_v<T&, U>
121
Box
&
operator
=(U&& other) {
122
if
(data_) {
123
*data_ = std::forward<U>(other);
124
}
else
{
125
data_ = std::make_unique<T>(std::forward<U>(other));
126
}
127
return
*
this
;
128
}
129
130
// Box is always engaged, unless moved-from. Just call *box.
131
/*implicit*/
operator
bool
()
const
=
delete
;
132
133
T* operator->()
noexcept
{
return
Get(); }
134
const
T* operator->()
const
noexcept
{
return
Get(); }
135
136
T& operator*()
noexcept
{
return
*Get(); }
137
const
T& operator*()
const
noexcept
{
return
*Get(); }
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
bool
operator<=(
const
Box
& other)
const
{
return
**
this
<= *other; }
148
149
bool
operator>=(
const
Box
& other)
const
{
return
**
this
>= *other; }
150
151
private
:
152
struct
EmplaceFactory
final
{};
153
154
template
<
typename
Factory>
155
explicit
Box(EmplaceFactory, Factory&& factory)
156
: data_(
new
T(std::forward<Factory>(factory)()))
157
{}
158
159
T* Get()
noexcept
{
160
UASSERT_MSG
(data_,
"Accessing a moved-from Box"
);
161
return
data_.get();
162
}
163
164
const
T* Get()
const
noexcept
{
165
UASSERT_MSG
(data_,
"Accessing a moved-from Box"
);
166
return
data_.get();
167
}
168
169
std::unique_ptr<T> data_;
170
};
171
172
template
<
typename
Value,
typename
T>
173
Box
<T> Parse(
const
Value& value, formats::
parse
::
To
<
Box
<T>>) {
174
return
Box
<T>::MakeWithFactory([&value] {
return
value.
template
As<T>(); });
175
}
176
177
template
<
typename
Value,
typename
T>
178
Value Serialize(
const
Box
<T>& value, formats::
serialize
::
To
<Value>) {
179
return
Serialize(*value, formats::
serialize
::
To
<Value>{});
180
}
181
182
template
<
typename
StringBuilder,
typename
T>
183
void
WriteToStream(
const
Box
<T>& value, StringBuilder& sw) {
184
WriteToStream(*value, sw);
185
}
186
187
template
<
typename
T>
188
logging
::LogHelper& operator<<(
logging
::LogHelper& lh,
const
Box
<T>& box) {
189
lh << *box;
190
return
lh;
191
}
192
193
}
// namespace utils
194
195
USERVER_NAMESPACE_END
userver
utils
box.hpp
Generated on
for userver by
Doxygen
1.17.0