Bond
 
Loading...
Searching...
No Matches
nullable.h
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#pragma once
5
6#include <bond/core/config.h>
7
8#include "container_interface.h"
9#include "detail/alloc.h"
10
11#include <boost/assert.hpp>
12#include <boost/optional.hpp>
13#include <boost/utility/enable_if.hpp>
14
15#include <memory>
16#include <stdint.h>
17
18namespace bond
19{
20
21namespace detail
22{
23
24template<typename T, typename Enable = void> struct
25allocator_type
26{
27 using type = std::allocator<T>;
28};
29
30template<typename T> struct
31allocator_type<T, typename boost::enable_if<std::is_class<typename T::allocator_type> >::type>
32{
33 using type = typename T::allocator_type;
34};
35
36
37template <typename T> struct
38use_value
39 : std::integral_constant<bool,
40 is_list_container<T>::value
41 || is_set_container<T>::value
42 || is_map_container<T>::value
43 || is_string<T>::value
44 || is_wstring<T>::value
45 || !std::is_class<T>::value> {};
46
47template<class T>
48BOND_CONSTEXPR inline T* to_address(T* ptr) BOND_NOEXCEPT
49{
50 return ptr;
51}
52
53template<class Ptr>
54inline typename std::pointer_traits<Ptr>::element_type*
55to_address(const Ptr& ptr) BOND_NOEXCEPT
56{
57 return bond::detail::to_address(ptr.operator->());
58}
59
60} // namespace detail
61
62
63//
64// Nullable value
65//
66template <typename T, typename Enable = void>
67class nullable;
68
69template <typename T>
70class nullable<T, typename boost::enable_if<detail::use_value<T> >::type>
71 : private detail::allocator_holder<typename detail::allocator_type<T>::type>
72{
73 using allocator_holder = typename nullable::allocator_holder;
74
75public:
76 using value_type = T;
77 using allocator_type = typename detail::allocator_type<T>::type;
78
79 nullable() = default;
80
81 explicit
82 nullable(const allocator_type& alloc)
83 : allocator_holder(alloc)
84 {}
85
86 nullable(const nullable& other) = default;
87
88 nullable(const nullable& other, const allocator_type& alloc)
89 : allocator_holder(alloc)
90 {
91 if (other.hasvalue())
92 {
93 _value.emplace(*other._value, alloc);
94 }
95 }
96
97 nullable(nullable&& other) BOND_NOEXCEPT_IF(
98 std::is_nothrow_move_constructible<allocator_holder>::value
99 && std::is_nothrow_move_constructible<T>::value)
100 : allocator_holder(std::move(other)),
101 _value(std::move(other._value))
102 {
103 other._value = boost::none; // assigning boost::none is noexcept
104 }
105
106 nullable(nullable&& other, const allocator_type& alloc)
107 : allocator_holder(alloc),
108 _value(std::move(other._value), alloc)
109 {
110 other._value = boost::none; // assigning boost::none is noexcept
111 }
112
113 explicit
114 nullable(const T& value)
115 : allocator_holder(get_allocator(value)),
116 _value(value)
117 {}
118
119 explicit
120 nullable(T&& value)
121 : allocator_holder(get_allocator(value)),
122 _value(std::move(value))
123 {}
124
125 nullable& operator=(const nullable& src) = default;
126
127 nullable& operator=(nullable&& other) BOND_NOEXCEPT_IF(
128 std::is_nothrow_move_constructible<allocator_holder>::value
129 && std::is_nothrow_move_constructible<T>::value)
130 {
131 allocator_holder::operator=(std::move(other));
132 _value = std::move(other._value);
133 other._value = boost::none; // assigning boost::none is noexcept
134 return *this;
135 }
136
137 bool hasvalue() const BOND_NOEXCEPT
138 {
139 return static_cast<bool>(_value);
140 }
141
142 bool empty() const BOND_NOEXCEPT
143 {
144 return !hasvalue();
145 }
146
147 explicit operator bool() const BOND_NOEXCEPT
148 {
149 return hasvalue();
150 }
151
152 T& value() BOND_NOEXCEPT
153 {
154 BOOST_ASSERT(hasvalue());
155 return *_value;
156 }
157
158 const T& value() const BOND_NOEXCEPT
159 {
160 BOOST_ASSERT(hasvalue());
161 return *_value;
162 }
163
164 T& operator*() BOND_NOEXCEPT
165 {
166 return value();
167 }
168
169 const T& operator*() const BOND_NOEXCEPT
170 {
171 return value();
172 }
173
174 T* operator->() BOND_NOEXCEPT
175 {
176 return &value();
177 }
178
179 const T* operator->() const BOND_NOEXCEPT
180 {
181 return &value();
182 }
183
184 T& set()
185 {
186 if (empty())
187 {
188 set_value();
189 }
190
191 return *_value;
192 }
193
194 void set(const T& value) BOND_NOEXCEPT_IF(std::is_nothrow_copy_constructible<T>::value
195 && std::is_nothrow_copy_assignable<T>::value)
196 {
197 _value = value;
198 }
199
200 void set(T&& value) BOND_NOEXCEPT_IF(std::is_nothrow_move_constructible<T>::value
201 && std::is_nothrow_move_assignable<T>::value)
202 {
203 _value = std::move(value);
204 }
205
206 void reset() BOND_NOEXCEPT
207 {
208 _value = boost::none;
209 }
210
211 void clear() BOND_NOEXCEPT
212 {
213 reset();
214 }
215
216 void swap(nullable& other)
217 {
218 using std::swap;
219 swap(static_cast<allocator_holder&>(*this), static_cast<allocator_holder&>(other));
220 swap(_value, other._value);
221 }
222
223 allocator_type get_allocator() const BOND_NOEXCEPT
224 {
225 return allocator_holder::get();
226 }
227
228private:
229 template <typename U = T>
230 typename boost::enable_if<std::uses_allocator<U, allocator_type> >::type
231 set_value()
232 {
233 _value.emplace(allocator_holder::get());
234 }
235
236 template <typename U = T>
237 typename boost::disable_if<std::uses_allocator<U, allocator_type> >::type
238 set_value()
239 {
240 _value.emplace();
241 }
242
243 template <typename U = T>
244 typename boost::enable_if<std::uses_allocator<U, allocator_type>, allocator_type>::type
245 static get_allocator(const T& value)
246 {
247 return value.get_allocator();
248 }
249
250 template <typename U = T>
251 typename boost::disable_if<std::uses_allocator<U, allocator_type>, allocator_type>::type
252 static get_allocator(const T& /*value*/)
253 {
254 return allocator_type();
255 }
256
257 boost::optional<T> _value;
258};
259
260
263template <typename T>
264class nullable<T, typename boost::disable_if<detail::use_value<T> >::type>
265 : private detail::allocator_holder<typename detail::allocator_type<T>::type>
266{
267public:
268 using value_type = T;
269 using allocator_type = typename detail::allocator_type<T>::type;
270
271private:
272 using allocator_holder = typename nullable::allocator_holder;
273 using rebind_alloc = typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
274 using pointer = typename std::allocator_traits<rebind_alloc>::pointer;
275
276public:
278 nullable() BOND_NOEXCEPT_IF(
279 std::is_nothrow_default_constructible<allocator_holder>::value
280 && std::is_nothrow_default_constructible<pointer>::value)
281 : allocator_holder(),
282 _value()
283 {}
284
286 explicit
287 nullable(const allocator_type& alloc)
288 : allocator_holder(alloc),
289 _value()
290 {}
291
293 nullable(const nullable& other)
294 : allocator_holder(other),
295 _value(other.hasvalue() ? new_value(other.value()) : pointer())
296 {}
297
298 nullable(const nullable& other, const allocator_type& alloc)
299 : allocator_holder(alloc),
300 _value(other.hasvalue() ? new_value(other.value(), alloc) : pointer())
301 {}
302
303 nullable(nullable&& other) BOND_NOEXCEPT_IF(
304 std::is_nothrow_move_constructible<allocator_holder>::value
305 && std::is_nothrow_move_constructible<pointer>::value
306 && BOND_NOEXCEPT(other._value = {}))
307 : allocator_holder(std::move(other)),
308 _value(std::move(other._value))
309 {
310 other._value = {};
311 }
312
313 nullable(nullable&& other, const allocator_type& alloc)
314 : allocator_holder(alloc),
315 _value(other.allocator_holder::get() == alloc
316 ? std::move(other._value)
317 : (other.hasvalue() ? new_value(std::move(*other._value), alloc) : pointer()))
318 {
319 other._value = {};
320 }
321
323 explicit
324 nullable(const T& value, const allocator_type& alloc = {})
325 : allocator_holder(alloc),
326 _value(new_value(value))
327 {}
328
329 explicit
330 nullable(T&& value, const allocator_type& alloc = {})
331 : allocator_holder(alloc),
332 _value(new_value(std::move(value)))
333 {}
334
336 nullable& operator=(const nullable& other)
337 {
338 nullable(other).swap(*this);
339 return *this;
340 }
341
342 nullable& operator=(nullable&& other)
343 {
344 nullable(std::move(other)).swap(*this);
345 return *this;
346 }
347
348 ~nullable()
349 {
350 reset();
351 }
352
353 bool hasvalue() const BOND_NOEXCEPT
354 {
355 return _value != pointer();
356 }
357
359 bool empty() const BOND_NOEXCEPT
360 {
361 return !hasvalue();
362 }
363
364 explicit operator bool() const BOND_NOEXCEPT
365 {
366 return hasvalue();
367 }
368
372 T& value() BOND_NOEXCEPT
373 {
374 BOOST_ASSERT(hasvalue());
375 return *_value;
376 }
377
381 const T& value() const BOND_NOEXCEPT
382 {
383 BOOST_ASSERT(hasvalue());
384 return *_value;
385 }
386
390 T& operator*() BOND_NOEXCEPT
391 {
392 return value();
393 }
394
398 const T& operator*() const BOND_NOEXCEPT
399 {
400 return value();
401 }
402
403 T* operator->() BOND_NOEXCEPT
404 {
405 return &value();
406 }
407
408 const T* operator->() const BOND_NOEXCEPT
409 {
410 return &value();
411 }
412
414 T& set()
415 {
416 if (empty())
417 {
418 _value = set_value();
419 }
420
421 return *_value;
422 }
423
425 void set(const T& value)
426 {
427 set_value(value);
428 }
429
430 void set(T&& value)
431 {
432 set_value(std::move(value));
433 }
434
436 void reset()
437 {
438 if (hasvalue())
439 {
440 delete_value();
441 _value = {};
442 }
443 }
444
446 void clear()
447 {
448 reset();
449 }
450
451 void swap(nullable& other)
452 {
453 using std::swap;
454 swap(static_cast<allocator_holder&>(*this), static_cast<allocator_holder&>(other));
455 swap(_value, other._value);
456 }
457
458 allocator_type get_allocator() const BOND_NOEXCEPT
459 {
460 return allocator_holder::get();
461 }
462
463private:
464 void delete_value()
465 {
466 rebind_alloc alloc(allocator_holder::get());
467 std::allocator_traits<rebind_alloc>::destroy(alloc, bond::detail::to_address(_value));
468 alloc.deallocate(_value, 1);
469 }
470
471 template <typename... Args>
472 pointer new_value(Args&&... args)
473 {
474 rebind_alloc alloc(allocator_holder::get());
475 pointer p(alloc.allocate(1));
476 try
477 {
478 std::allocator_traits<rebind_alloc>::construct(
479 alloc, bond::detail::to_address(p), std::forward<Args>(args)...);
480 return p;
481 }
482 catch (...)
483 {
484 alloc.deallocate(p, 1);
485 throw;
486 }
487 }
488
489 template <typename U = T>
490 typename boost::enable_if<std::uses_allocator<U, allocator_type>, pointer>::type
491 set_value()
492 {
493 return new_value(allocator_holder::get());
494 }
495
496 template <typename U = T>
497 typename boost::disable_if<std::uses_allocator<U, allocator_type>, pointer>::type
498 set_value()
499 {
500 return new_value();
501 }
502
503 template <typename U>
504 void set_value(U&& value)
505 {
506 if (empty())
507 {
508 _value = new_value(std::forward<U>(value));
509 }
510 else
511 {
512 *_value = std::forward<U>(value);
513 }
514 }
515
516 pointer _value;
517};
518
519
520template <typename T>
521inline void swap(nullable<T>& x, nullable<T>& y)
522{
523 x.swap(y);
524}
525
526
527template <typename T>
528inline bool operator==(const nullable<T>& x, const nullable<T>& y)
529{
530 return (x.hasvalue() == y.hasvalue() && (!x.hasvalue() || *x == *y));
531}
532
533
534template <typename T>
535inline bool operator!=(const nullable<T>& x, const nullable<T>& y)
536{
537 return !(x == y);
538}
539
540
541// nullable<T> is internally treated as a list container with 0 or 1 element
542
543// container_size
544template <typename T>
545uint32_t container_size(const nullable<T>& value)
546{
547 return value.empty() ? 0 : 1;
548}
549
550
551// resize_list
552template <typename T>
553void resize_list(nullable<T>& value, uint32_t size)
554{
555 if (size)
556 {
557 value.set();
558 }
559 else
560 {
561 value.reset();
562 }
563}
564
565
566template <typename T> struct
567element_type<nullable<T> >
568{
569 typedef T type;
570};
571
572
573// enumerators
574template <typename T>
575class const_enumerator<nullable<T> >
576{
577public:
578 const_enumerator(const nullable<T>& value)
579 : _value(value),
580 _more(value.hasvalue())
581 {}
582
583 const_enumerator(const const_enumerator& other) = delete;
584 const_enumerator& operator=(const const_enumerator& other) = delete;
585
586 bool more()
587 {
588 return _more;
589 }
590
591 const T& next()
592 {
593 _more = false;
594 return _value.value();
595 }
596
597private:
598 const nullable<T>& _value;
599 bool _more;
600};
601
602
603template <typename T>
604class enumerator<nullable<T> >
605{
606public:
607 enumerator(nullable<T>& value)
608 : _value(value),
609 _more(value.hasvalue())
610 {}
611
612 enumerator(const enumerator& other) = delete;
613 enumerator& operator=(const enumerator& other) = delete;
614
615 bool more()
616 {
617 return _more;
618 }
619
620 T& next()
621 {
622 _more = false;
623 return _value.value();
624 }
625
626private:
627 nullable<T>& _value;
628 bool _more;
629};
630
631
632template <typename T> struct
633is_list_container<nullable<T> >
634 : std::true_type {};
635
636
637} // namespace bond
Helper type that holds an allocator.
Definition: alloc.h:22
void set(const T &value)
Set to specified value.
Definition: nullable.h:425
bool empty() const BOND_NOEXCEPT
Checks if the object is null.
Definition: nullable.h:359
T & value() BOND_NOEXCEPT
Return reference to contained value.
Definition: nullable.h:372
nullable(const T &value, const allocator_type &alloc={})
Construct from an instance T.
Definition: nullable.h:324
const T & operator*() const BOND_NOEXCEPT
Dereference operator.
Definition: nullable.h:398
nullable() BOND_NOEXCEPT_IF(std
Default constructor.
Definition: nullable.h:278
nullable & operator=(const nullable &other)
Assignment operator.
Definition: nullable.h:336
T & set()
Set to default instance of T and return reference to the value.
Definition: nullable.h:414
nullable(const allocator_type &alloc)
Construct nullable using specified allocator instance.
Definition: nullable.h:287
const T & value() const BOND_NOEXCEPT
Return constant reference to contained value.
Definition: nullable.h:381
nullable(const nullable &other)
Copy constructor.
Definition: nullable.h:293
T & operator*() BOND_NOEXCEPT
Dereference operator.
Definition: nullable.h:390
namespace bond
Definition: apply.h:17
void swap(blob &src, blob &dst) BOND_NOEXCEPT
Swap two blobs.
Definition: blob.h:277
boost::enable_if< is_signed_int_or_enum< SignedT >, bool >::type operator==(const Variant &variant, SignedT value)
Compares variant for equality against the provided signed integer or enum value.
Definition: metadata.h:24
STL namespace.