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 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
template
<
typename
U = T>
63
// Protect against hiding special constructors.
64
requires
(!std::same_as<std::remove_cvref_t<U>,
Box
>) &&
65
// Prevent infinite recursion from constructible_from in the next check.
66
(!impl::IsBox<std::remove_cvref_t<U>>::value) &&
67
// Prevent infinite recursion.
68
(std::same_as<std::remove_cvref_t<U>, T> || !std::is_constructible_v<T,
Box
>) &&
69
// Normal requirement.
70
std::is_constructible_v<T, U>
71
explicit
(!
std
::
convertible_to
<
U
,
T
>)
Box
(U&& arg)
72
: data_(std::make_unique<T>(std::forward<U>(arg)))
73
{}
74
75
/// Allocate the value, emplacing it with the given @a args.
76
template
<
typename
... Args>
77
requires
(
sizeof
...(Args) >= 2) && std::is_constructible_v<T, Args...>
78
explicit
Box
(Args&&... args)
79
: data_(std::make_unique<T>(std::forward<Args>(args)...))
80
{}
81
82
/// Allocate the value as constructed by the given @a factory.
83
/// Allows to save an extra move of the contained value.
84
template
<
typename
Factory>
85
static
Box
MakeWithFactory
(Factory&& factory) {
86
return
Box
(EmplaceFactory{}, std::forward<Factory>(factory));
87
}
88
89
Box(
Box
&& other)
noexcept
=
default
;
90
Box
& operator=(
Box
&& other)
noexcept
=
default
;
91
92
Box(
const
Box
& other)
93
: data_(std::make_unique<T>(*other))
94
{}
95
96
Box
& operator=(
const
Box
& other) {
97
*
this
=
Box
{other};
98
return
*
this
;
99
}
100
101
/// Assigns-through to the contained value.
102
template
<
typename
U = T>
103
requires
(!std::same_as<std::remove_cvref_t<U>,
Box
>) && std::is_assignable_v<T&, U>
104
Box
&
operator
=(U&& other) {
105
if
(data_) {
106
*data_ = std::forward<U>(other);
107
}
else
{
108
data_ = std::make_unique<T>(std::forward<U>(other));
109
}
110
return
*
this
;
111
}
112
113
// Box is always engaged, unless moved-from. Just call *box.
114
/*implicit*/
operator
bool
()
const
=
delete
;
115
116
T* operator->()
noexcept
{
return
Get(); }
117
const
T* operator->()
const
noexcept
{
return
Get(); }
118
119
T& operator*()
noexcept
{
return
*Get(); }
120
const
T& operator*()
const
noexcept
{
return
*Get(); }
121
122
bool
operator==(
const
Box
& other)
const
{
return
**
this
== *other; }
123
124
bool
operator!=(
const
Box
& other)
const
{
return
**
this
!= *other; }
125
126
bool
operator<(
const
Box
& other)
const
{
return
**
this
< *other; }
127
128
bool
operator>(
const
Box
& other)
const
{
return
**
this
> *other; }
129
130
bool
operator<=(
const
Box
& other)
const
{
return
**
this
<= *other; }
131
132
bool
operator>=(
const
Box
& other)
const
{
return
**
this
>= *other; }
133
134
private
:
135
struct
EmplaceFactory
final
{};
136
137
template
<
typename
Factory>
138
explicit
Box(EmplaceFactory, Factory&& factory)
139
: data_(
new
T(std::forward<Factory>(factory)()))
140
{}
141
142
T* Get()
noexcept
{
143
UASSERT_MSG
(data_,
"Accessing a moved-from Box"
);
144
return
data_.get();
145
}
146
147
const
T* Get()
const
noexcept
{
148
UASSERT_MSG
(data_,
"Accessing a moved-from Box"
);
149
return
data_.get();
150
}
151
152
std::unique_ptr<T> data_;
153
};
154
155
template
<
typename
Value,
typename
T>
156
Box
<T> Parse(
const
Value& value, formats::
parse
::
To
<
Box
<T>>) {
157
return
Box
<T>::MakeWithFactory([&value] {
return
value.
template
As<T>(); });
158
}
159
160
template
<
typename
Value,
typename
T>
161
Value Serialize(
const
Box
<T>& value, formats::
serialize
::
To
<Value>) {
162
return
Serialize(*value, formats::
serialize
::
To
<Value>{});
163
}
164
165
template
<
typename
StringBuilder,
typename
T>
166
void
WriteToStream(
const
Box
<T>& value, StringBuilder& sw) {
167
WriteToStream(*value, sw);
168
}
169
170
template
<
typename
T>
171
logging::LogHelper& operator<<(logging::LogHelper& lh,
const
Box
<T>& box) {
172
lh << *box;
173
return
lh;
174
}
175
176
}
// namespace utils
177
178
USERVER_NAMESPACE_END
userver
utils
box.hpp
Generated on
for userver by
Doxygen
1.17.0