-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompact_optional.hpp
404 lines (329 loc) · 13 KB
/
compact_optional.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
// Copyright (C) 2015, Andrzej Krzemienski.
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef AK_TOOLBOX_COMPACT_OPTIONAL_HEADER_GUARD_
#define AK_TOOLBOX_COMPACT_OPTIONAL_HEADER_GUARD_
#include <cassert>
#include <utility>
#include <limits>
#include <new>
# include <type_traits>
#if defined AK_TOOLBOX_NO_ARVANCED_CXX11
# define AK_TOOLBOX_NOEXCEPT
# define AK_TOOLBOX_CONSTEXPR
# define AK_TOOLBOX_EXPLICIT_CONV
# define AK_TOOLBOX_NOEXCEPT_AS(E)
#else
# define AK_TOOLBOX_NOEXCEPT noexcept
# define AK_TOOLBOX_CONSTEXPR constexpr
# define AK_TOOLBOX_EXPLICIT_CONV explicit
# define AK_TOOLBOX_NOEXCEPT_AS(E) noexcept(noexcept(E))
# define AK_TOOLBOX_CONSTEXPR_NOCONST // fix in the future
#endif
#if defined NDEBUG
# define AK_TOOLBOX_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR)
#elif defined __clang__ || defined __GNU_LIBRARY__
# define AK_TOOLBOX_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : (fail(#CHECK, __FILE__, __LINE__), (EXPR)))
inline void fail(const char* expr, const char* file, int line)
{
__assert(expr, file, line);
}
#elif defined __GNUC__
# define AK_TOOLBOX_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : (fail(#CHECK, __FILE__, __LINE__), (EXPR)))
inline void fail(const char* expr, const char* file, unsigned line)
{
_assert(expr, file, line);
}
#else
# error UNSUPPORTED COMPILER
#endif
namespace ak_toolbox {
namespace compact_optional_ns {
struct default_tag{};
template <typename T, typename NT = T, typename CREF = const T&>
struct compact_optional_type
{
typedef T value_type;
typedef NT storage_type;
typedef CREF reference_type;
static AK_TOOLBOX_CONSTEXPR const value_type& access_value(const storage_type& v) { return v; }
static AK_TOOLBOX_CONSTEXPR const value_type& store_value(const value_type& v) { return v; }
static AK_TOOLBOX_CONSTEXPR value_type&& store_value(value_type&& v) { return std::move(v); }
};
template <typename T, T Val>
struct evp_int : compact_optional_type<T>
{
static AK_TOOLBOX_CONSTEXPR T empty_value() AK_TOOLBOX_NOEXCEPT { return Val; }
static AK_TOOLBOX_CONSTEXPR bool is_empty_value(T v) { return v == Val; }
};
// for backward compatibility only:
template <typename T, T Val>
struct empty_scalar_value : compact_optional_type<T>
{
static AK_TOOLBOX_CONSTEXPR T empty_value() AK_TOOLBOX_NOEXCEPT { return Val; }
static AK_TOOLBOX_CONSTEXPR bool is_empty_value(T v) { return v == Val; }
};
template <typename FPT>
struct evp_fp_nan : compact_optional_type<FPT>
{
static AK_TOOLBOX_CONSTEXPR FPT empty_value() AK_TOOLBOX_NOEXCEPT { return std::numeric_limits<FPT>::quiet_NaN(); }
static AK_TOOLBOX_CONSTEXPR bool is_empty_value(FPT v) { return v != v; }
};
template <typename T> // requires Regular<T>
struct evp_value_init : compact_optional_type<T>
{
static AK_TOOLBOX_CONSTEXPR T empty_value() AK_TOOLBOX_NOEXCEPT_AS(T()) { return T(); }
static AK_TOOLBOX_CONSTEXPR bool is_empty_value(const T& v) { return v == T(); }
};
template <typename T>
struct evp_stl_empty : compact_optional_type<T>
{
static AK_TOOLBOX_CONSTEXPR T empty_value() AK_TOOLBOX_NOEXCEPT_AS(T()) { return T(); }
static AK_TOOLBOX_CONSTEXPR bool is_empty_value(const T& v) { return v.empty(); }
};
template <typename OT>
struct evp_optional : compact_optional_type<typename OT::value_type, OT>
{
typedef typename OT::value_type value_type;
typedef OT storage_type;
static OT empty_value() AK_TOOLBOX_NOEXCEPT { return OT(); }
static bool is_empty_value(const OT& v) { return !v; }
static const value_type& access_value(const storage_type& v) { return *v; }
static storage_type store_value(const value_type& v) { return v; }
static storage_type store_value(value_type&& v) { return std::move(v); }
};
// for backwards compatibility only:
template <typename OT>
struct compact_optional_from_optional : compact_optional_type<typename OT::value_type, OT>
{
typedef typename OT::value_type value_type;
typedef OT storage_type;
static OT empty_value() AK_TOOLBOX_NOEXCEPT { return OT(); }
static bool is_empty_value(const OT& v) { return !v; }
static const value_type& access_value(const storage_type& v) { return *v; }
static storage_type store_value(const value_type& v) { return v; }
static storage_type store_value(value_type&& v) { return std::move(v); }
};
struct evp_bool : compact_optional_type<bool, char, bool>
{
static AK_TOOLBOX_CONSTEXPR char empty_value() AK_TOOLBOX_NOEXCEPT { return char(2); }
static AK_TOOLBOX_CONSTEXPR bool is_empty_value(char v) { return v == 2; }
static AK_TOOLBOX_CONSTEXPR bool access_value(const char& v) { return bool(v); }
static AK_TOOLBOX_CONSTEXPR char store_value(const bool& v) { return v; }
};
typedef evp_bool compact_bool;
struct compact_optional_pod_storage_type_tag{};
#ifndef AK_TOOLBOX_NO_ARVANCED_CXX11
template <typename T, typename POD_T = typename std::aligned_storage<sizeof(T), alignof(T)>::type>
#else
template <typename T, typename POD_T>
#endif // AK_TOOLBOX_NO_ARVANCED_CXX11
struct compact_optional_pod_storage_type : compact_optional_pod_storage_type_tag
{
static_assert(sizeof(T) == sizeof(POD_T), "pod storage for T has to have the same size and alignment as T");
static_assert(std::is_pod<POD_T>::value, "second argument must be a POD type");
#ifndef AK_TOOLBOX_NO_ARVANCED_CXX11
static_assert(alignof(T) == alignof(POD_T), "pod storage for T has to have the same alignment as T");
#endif // AK_TOOLBOX_NO_ARVANCED_CXX11
typedef T value_type;
typedef POD_T storage_type;
typedef const T& reference_type;
static const value_type& access_value(const storage_type& s) { return reinterpret_cast<const value_type&>(s); }
static const storage_type& store_value(const value_type& v) { return reinterpret_cast<const storage_type&>(v); }
};
#ifndef AK_TOOLBOX_NO_UNDERLYING_TYPE
template <typename Enum, typename std::underlying_type<Enum>::type Val>
struct evp_enum : compact_optional_pod_storage_type<Enum, typename std::underlying_type<Enum>::type>
{
static_assert(std::is_enum<Enum>::value, "evp_enum only works with enum types");
typedef compact_optional_pod_storage_type<Enum, typename std::underlying_type<Enum>::type> base;
typedef typename base::storage_type storage_type;
static storage_type empty_value() { return Val; }
static bool is_empty_value(const storage_type& v) { return v == Val; }
};
#else
template <typename Enum, int Val>
struct evp_enum : compact_optional_pod_storage_type<Enum, int>
{
typedef compact_optional_pod_storage_type<Enum, int> base;
typedef typename base::storage_type storage_type;
static storage_type empty_value() { return Val; }
static bool is_empty_value(const storage_type& v) { return v == Val; }
};
#endif // AK_TOOLBOX_NO_UNDERLYING_TYPE
namespace detail_ {
template <typename EVP>
struct member_storage
{
typedef typename EVP::value_type value_type;
typedef typename EVP::storage_type storage_type;
typedef typename EVP::reference_type reference_type;
storage_type value_;
AK_TOOLBOX_CONSTEXPR member_storage() AK_TOOLBOX_NOEXCEPT_AS(storage_type(EVP::empty_value()))
: value_(EVP::empty_value()) {}
AK_TOOLBOX_CONSTEXPR member_storage(const value_type& v)
: value_(EVP::store_value(v)) {}
AK_TOOLBOX_CONSTEXPR member_storage(value_type&& v)
: value_(EVP::store_value(std::move(v))) {}
void swap_impl(member_storage& rhs)
{
using namespace std;
swap(value_, rhs.value_);
}
};
template <typename EVP>
struct buffer_storage
{
typedef typename EVP::value_type value_type;
typedef typename EVP::storage_type storage_type;
typedef typename EVP::reference_type reference_type;
storage_type value_;
private:
void* address() { return static_cast<void*>(std::addressof(value_)); }
void construct(const value_type& v) { ::new (address()) value_type(v); }
void construct(value_type&& v) { ::new (address()) value_type(std::move(v)); }
void call_destructor() { as_value_type().value_type::~value_type(); }
void destroy() { call_destructor(); value_ = EVP::empty_value(); } // TODO: "fill_empty_value_pattern"
bool has_value() const { return !EVP::is_empty_value(value_); }
value_type& as_value_type() { return reinterpret_cast<value_type&>(value_); }
const value_type& as_value_type() const { return reinterpret_cast<const value_type&>(value_); }
public:
buffer_storage() AK_TOOLBOX_NOEXCEPT_AS(storage_type(EVP::empty_value()))
: value_(EVP::empty_value()) {}
buffer_storage(const value_type& v) : value_(EVP::empty_value())
{ construct(v); }
buffer_storage(value_type&& v) : value_(EVP::empty_value())
{ construct(std::move(v)); }
buffer_storage(const buffer_storage& rhs) : value_(EVP::empty_value())
{
if (rhs.has_value())
construct(rhs.as_value_type());
}
buffer_storage(buffer_storage&& rhs) : value_(EVP::empty_value())
{
if (rhs.has_value())
construct(std::move(rhs.as_value_type())); // TODO: add move
}
void operator=(const buffer_storage& rhs)
{
if (has_value() && rhs.has_value())
{
as_value_type() = rhs.as_value_type();
}
else if (has_value() && !rhs.has_value())
{
destroy();
}
else if (!has_value() && rhs.has_value())
{
construct(rhs.as_value_type());
}
}
void operator=(buffer_storage&& rhs)
{
if (has_value() && rhs.has_value())
{
as_value_type() = std::move(rhs.as_value_type());
}
else if (has_value() && !rhs.has_value())
{
destroy();
}
else if (!has_value() && rhs.has_value())
{
construct(std::move(rhs.as_value_type()));
}
}
void swap_impl(buffer_storage& rhs)
{
using namespace std;
if (has_value() && rhs.has_value())
{
swap(as_value_type(), rhs.as_value_type());
}
else if (has_value() && !rhs.has_value())
{
rhs.construct(std::move(as_value_type()));
destroy();
}
else if (!has_value() && rhs.has_value())
{
construct(std::move(rhs.as_value_type()));
rhs.destroy();
}
}
~buffer_storage()
{
if (has_value())
call_destructor();
}
// TODO: implement moves and copies, swap, dtor
};
template <typename T>
struct storage_destruction
{
typedef typename std::conditional<std::is_base_of<compact_optional_pod_storage_type_tag, T>::value,
buffer_storage<T>,
member_storage<T>>::type type;
};
template <typename N>
class compact_optional_base : storage_destruction<N>::type
{
typedef typename storage_destruction<N>::type base;
base& as_base() { return static_cast<base&>(*this); }
protected:
typedef typename N::value_type value_type;
typedef typename N::storage_type storage_type;
typedef typename N::reference_type reference_type;
void swap_storages(compact_optional_base& rhs) { as_base().swap_impl(rhs.as_base()); }
AK_TOOLBOX_CONSTEXPR_NOCONST storage_type& raw_value() { return base::value_; }
public:
AK_TOOLBOX_CONSTEXPR compact_optional_base() AK_TOOLBOX_NOEXCEPT_AS(base())
: base() {}
AK_TOOLBOX_CONSTEXPR compact_optional_base(const value_type& v)
: base(v) {}
AK_TOOLBOX_CONSTEXPR compact_optional_base(value_type&& v)
: base(std::move(v)) {}
AK_TOOLBOX_CONSTEXPR bool has_value() const { return !N::is_empty_value(base::value_); }
AK_TOOLBOX_CONSTEXPR reference_type value() const { return AK_TOOLBOX_ASSERTED_EXPRESSION(has_value(), N::access_value(base::value_)); }
AK_TOOLBOX_CONSTEXPR storage_type const& unsafe_raw_value() const { return base::value_; }
};
} // namespace detail_
template <typename N, typename /* tag */ = default_tag>
class compact_optional : public detail_::compact_optional_base<N>
{
typedef detail_::compact_optional_base<N> super;
public:
typedef typename N::value_type value_type;
typedef typename N::storage_type storage_type;
typedef typename N::reference_type reference_type;
AK_TOOLBOX_CONSTEXPR compact_optional() AK_TOOLBOX_NOEXCEPT_AS(storage_type(N::empty_value()))
: super() {}
AK_TOOLBOX_CONSTEXPR compact_optional(const value_type& v)
: super(v) {}
AK_TOOLBOX_CONSTEXPR compact_optional(value_type&& v)
: super(std::move(v)) {}
friend void swap(compact_optional& l, compact_optional&r)
{
l.swap_storages(r);
}
};
} // namespace compact_optional_ns
using compact_optional_ns::compact_optional;
using compact_optional_ns::empty_scalar_value;
using compact_optional_ns::compact_optional_type;
using compact_optional_ns::compact_optional_pod_storage_type;
using compact_optional_ns::compact_optional_from_optional;
using compact_optional_ns::compact_bool;
using compact_optional_ns::evp_bool;
using compact_optional_ns::evp_int;
using compact_optional_ns::evp_fp_nan;
using compact_optional_ns::evp_value_init;
using compact_optional_ns::evp_optional;
using compact_optional_ns::evp_stl_empty;
using compact_optional_ns::evp_enum;
} // namespace ak_toolbox
#undef AK_TOOLBOX_ASSERTED_EXPRESSION
#endif //AK_TOOLBOX_COMPACT_OPTIONAL_HEADER_GUARD_