1 /*
2 * Copyright 2019 by its authors. See AUTHORS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #ifndef EINA_OPTIONAL_HH_
17 #define EINA_OPTIONAL_HH_
18
19 #include <cstddef>
20 #include <algorithm>
21 #include <utility>
22 #include <type_traits>
23
24 /**
25 * @addtogroup Eina_Cxx_Data_Types_Group
26 *
27 * @{
28 */
29
30 namespace efl_eina_swap_adl {
31
32 /**
33 * @internal
34 */
35 template <typename T>
swap_impl(T & lhs,T & rhs)36 void swap_impl(T& lhs, T& rhs)
37 {
38 using namespace std;
39 swap(lhs, rhs);
40 }
41
42 }
43
44 namespace efl { namespace eina {
45
46 /**
47 * @defgroup Eina_Cxx_Optional_Group Optional Value
48 * @ingroup Eina_Cxx_Data_Types_Group
49 *
50 * @{
51 */
52
53 /**
54 * @internal
55 */
56 template <typename T>
adl_swap(T & lhs,T & rhs)57 void adl_swap(T& lhs, T& rhs)
58 {
59 ::efl_eina_swap_adl::swap_impl<T>(lhs, rhs);
60 }
61
62 /**
63 * This class manages an optional contained value, i.e. a value that
64 * semantically may not be present.
65 *
66 * A common use case for optional is the return value of a function that
67 * may fail. As opposed to other approaches, such as
68 * <tt>std::pair<T,bool></tt>, optional handles expensive to construct
69 * objects well and is more readable, as the intent is expressed
70 * explicitly.
71 *
72 * An optional object holding a semantically present value is considered
73 * to be @em engaged, otherwise it is considered to be @em disengaged.
74 */
75 template <typename T>
76 struct optional
77 {
78 typedef optional<T> _self_type; /**< Type for the optional class itself. */
79
80 /**
81 * @brief Create a disengaged object.
82 *
83 * This constructor creates a disengaged <tt>eina::optional</tt>
84 * object.
85 */
optionalefl::eina::optional86 constexpr optional(std::nullptr_t) : engaged(false)
87 {}
88
89 /**
90 * @brief Default constructor. Create a disengaged object.
91 */
optionalefl::eina::optional92 constexpr optional() : engaged(false)
93 {}
94
95 /**
96 * @brief Create an engaged object by moving @p other content.
97 * @param other R-value reference to the desired type.
98 *
99 * This constructor creates an <tt>eina::optional</tt> object in an
100 * engaged state. The contained value is initialized by moving
101 * @p other.
102 */
optionalefl::eina::optional103 optional(T&& other) : engaged(false)
104 {
105 _construct(std::forward<T>(other));
106 }
107
108 /**
109 * @brief Create an engaged object by copying @p other content.
110 * @param other Constant reference to the desired type.
111 *
112 * This constructor creates an <tt>eina::optional</tt> object in an
113 * engaged state. The contained value is initialized by copying
114 * @p other.
115 */
optionalefl::eina::optional116 optional(T const& other) : engaged(false)
117 {
118 _construct(other);
119 }
120
121 /**
122 * @brief Create an engaged object by moving @p other content.
123 * @param other R-value reference to the desired type.
124 *
125 * This constructor creates an <tt>eina::optional</tt> object in an
126 * engaged state. The contained value is initialized by moving
127 * @p other.
128 */
129 template <typename U>
optionalefl::eina::optional130 optional(U&& other, typename std::enable_if<std::is_convertible<U, T>::value>::type* = 0) : engaged(false)
131 {
132 _construct(std::forward<U>(other));
133 }
134
135 /**
136 * @brief Create an engaged object by copying @p other content.
137 * @param other Constant reference to the desired type.
138 *
139 * This constructor creates an <tt>eina::optional</tt> object in an
140 * engaged state. The contained value is initialized by copying
141 * @p other.
142 */
143 template <typename U>
optionalefl::eina::optional144 optional(U const& other, typename std::enable_if<std::is_convertible<U, T>::value>::type* = 0) : engaged(false)
145 {
146 _construct(other);
147 }
148
149 /**
150 * @brief Copy constructor. Create an object containing the same value as @p other and in the same state.
151 * @param other Constant reference to another <tt>eina::optional</tt> object that holds the same value type.
152 *
153 * This constructor creates an <tt>eina::optional</tt> object with
154 * the same engagement state of @p other. If @p other is engaged then
155 * the contained value of the newly created object is initialized by
156 * copying the contained value of @p other.
157 */
optionalefl::eina::optional158 optional(optional<T> const& other)
159 : engaged(false)
160 {
161 if(other.engaged) _construct(*other);
162 }
163
164 /**
165 * @brief Move constructor. Create an object containing the same value as @p other and in the same state.
166 * @param other R-value reference to another <tt>eina::optional</tt> object that holds the same value type.
167 *
168 * This constructor creates an <tt>eina::optional</tt> object with
169 * the same engagement state of @p other. If @p other is engaged then
170 * the contained value of the newly created object is initialized by
171 * moving the contained value of @p other.
172 */
optionalefl::eina::optional173 optional(optional<T>&& other)
174 : engaged(false)
175 {
176 if(other.engaged) _construct(std::move(*other));
177 other._destroy();
178 }
179
180 /**
181 * @brief Move constructor. Create an object containing the same value as @p other and in the same state.
182 * @param other R-value reference to another <tt>eina::optional</tt> object that holds a different, but convertible, value type.
183 *
184 * This constructor creates an <tt>eina::optional</tt> object with
185 * the same engagement state of @p other. If @p other is engaged then
186 * the contained value of the newly created object is initialized by
187 * moving the contained value of @p other.
188 */
189 template <typename U>
optionalefl::eina::optional190 optional(optional<U>&& other, typename std::enable_if<std::is_convertible<U, T>::value>::type* = 0)
191 : engaged(false)
192 {
193 if (other.is_engaged()) _construct(std::move(*other));
194 other.disengage();
195 }
196
197 /**
198 * @brief Copy constructor. Create an object containing the same value as @p other and in the same state.
199 * @param other Constant reference to another <tt>eina::optional</tt> object that holds a different, but convertible, value type.
200 *
201 * This constructor creates an <tt>eina::optional</tt> object with
202 * the same engagement state of @p other. If @p other is engaged then
203 * the contained value of the newly created object is initialized by
204 * converting and copying the contained value of @p other.
205 */
206 template <typename U>
optionalefl::eina::optional207 optional(optional<U> const& other, typename std::enable_if<std::is_convertible<U, T>::value>::type* = 0)
208 : engaged(false)
209 {
210 if (other.is_engaged()) _construct(*other);
211 }
212
213 /**
214 * @brief Assign new content to the object.
215 * @param other R-value reference to another <tt>eina::optional</tt> object that holds the same value type.
216 *
217 * This operator replaces the current content of the object. If
218 * @p other is engaged its contained value is moved to this object,
219 * making <tt>*this</tt> be considered engaged too. If @p other is
220 * disengaged <tt>*this</tt> is also made disengaged and its
221 * contained value, if any, is simple destroyed.
222 */
operator =efl::eina::optional223 _self_type& operator=(optional<T>&& other)
224 {
225 _destroy();
226 if (other.engaged)
227 _construct(std::move(*other));
228 other._destroy();
229 return *this;
230 }
231
232 /**
233 * @brief Assign new content to the object.
234 * @param other Constant reference to another <tt>eina::optional</tt> object that holds the same value type.
235 *
236 * This operator replaces the current content of the object. If
237 * @p other is engaged its contained value is copied to this object,
238 * making <tt>*this</tt> be considered engaged too. If @p other is
239 * disengaged <tt>*this</tt> is also made disengaged and its
240 * contained value, if any, is simple destroyed.
241 */
operator =efl::eina::optional242 _self_type& operator=(optional<T>const& other)
243 {
244 optional<T> tmp(other);
245 tmp.swap(*this);
246 return *this;
247 }
248
249 /**
250 * @brief Assign new content to the object.
251 * @param other R-value reference to another <tt>eina::optional</tt> object that holds a different, but convertible, value type.
252 *
253 * This operator replaces the current content of the object. If
254 * @p other is engaged its contained value is moved to this object,
255 * making <tt>*this</tt> be considered engaged too. If @p other is
256 * disengaged <tt>*this</tt> is also made disengaged and its
257 * contained value, if any, is simple destroyed.
258 */
259 template <typename U>
operator =efl::eina::optional260 typename std::enable_if<std::is_convertible<U, T>::value, _self_type>::type& operator=(optional<U>&& other)
261 {
262 _destroy();
263 if (other.is_engaged())
264 _construct(std::move(*other));
265 other.disengage();
266 return *this;
267 }
268
269 /**
270 * @brief Assign new content to the object.
271 * @param other Constant reference to another <tt>eina::optional</tt> object that holds a different, but convertible, value type.
272 *
273 * This operator replaces the current content of the object. If
274 * @p other is engaged its contained value is converted and copied to this
275 * object, making <tt>*this</tt> be considered engaged too. If @p other is
276 * disengaged <tt>*this</tt> is also made disengaged and its
277 * contained value, if any, is simple destroyed.
278 */
279 template <typename U>
operator =efl::eina::optional280 typename std::enable_if<std::is_convertible<U, T>::value, _self_type>::type& operator=(optional<U>const& other)
281 {
282 _destroy();
283 if (other.is_engaged())
284 _construct(*other);
285 return *this;
286 }
287
288 /**
289 * @brief Disengage the object, destroying the current contained value, if any.
290 */
disengageefl::eina::optional291 void disengage()
292 {
293 _destroy();
294 }
295
296 /**
297 * @brief Releases the contained value if the object is engaged.
298 */
~optionalefl::eina::optional299 ~optional()
300 {
301 _destroy();
302 }
303
304 /**
305 * @brief Convert to @c bool based on whether the object is engaged or not.
306 * @return @c true if the object is engaged, @c false otherwise.
307 */
operator boolefl::eina::optional308 explicit operator bool() const
309 {
310 return is_engaged();
311 }
312
313 /**
314 * @brief Convert to @c bool based on whether the object is engaged or not.
315 * @return @c true if the object is disengaged, @c false otherwise.
316 */
operator !efl::eina::optional317 bool operator!() const
318 {
319 bool b ( *this );
320 return !b;
321 }
322
323 /**
324 * @brief Access member of the contained value.
325 * @return Pointer to the contained value, whose member will be accessed.
326 */
operator ->efl::eina::optional327 T* operator->()
328 {
329 assert(is_engaged());
330 return static_cast<T*>(static_cast<void*>(&buffer));
331 }
332
333 /**
334 * @brief Access constant member of the contained value.
335 * @return Constant pointer to the contained value, whose member will be accessed.
336 */
operator ->efl::eina::optional337 T const* operator->() const
338 {
339 return const_cast<_self_type&>(*this).operator->();
340 }
341
342 /**
343 * @brief Get the contained value.
344 * @return Reference to the contained value.
345 */
operator *efl::eina::optional346 T& operator*() { return get(); }
347
348 /**
349 * @brief Get the contained value.
350 * @return Constant reference to the contained value.
351 */
operator *efl::eina::optional352 T const& operator*() const { return get(); }
353
354 /**
355 * @brief Get the contained value.
356 * @return Reference to the contained value.
357 */
getefl::eina::optional358 T& get() { return *this->operator->(); }
359
360 /**
361 * @brief Get the contained value.
362 * @return Constant reference to the contained value.
363 */
getefl::eina::optional364 T const& get() const { return *this->operator->(); }
365
366 /**
367 * @brief Swap content with another <tt>eina::optional</tt> object.
368 * @param other Another <tt>eina::optional</tt> object.
369 */
swapefl::eina::optional370 void swap(optional<T>& other)
371 {
372 if(is_engaged() && other.is_engaged())
373 {
374 eina::adl_swap(**this, *other);
375 }
376 else if(is_engaged())
377 {
378 other._construct(std::move(**this));
379 _destroy();
380 }
381 else if(other.is_engaged())
382 {
383 _construct(std::move(*other));
384 other._destroy();
385 }
386 }
387
388 /**
389 * @brief Check if the object is engaged.
390 * @return @c true if the object is currently engaged, @c false otherwise.
391 */
is_engagedefl::eina::optional392 bool is_engaged() const
393 {
394 return engaged;
395 }
396 private:
397
398 /**
399 * @internal
400 */
401 template <typename U>
_constructefl::eina::optional402 void _construct(U&& object)
403 {
404 assert(!is_engaged());
405 // NOTE: the buffer memory is intended to be in an
406 // uninitialized state here.
407 // So this warning can be disabled.
408 #pragma GCC diagnostic push
409 #ifndef __clang__
410 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
411 #endif
412 new (&buffer) T(std::forward<U>(object));
413 #pragma GCC diagnostic pop
414 engaged = true;
415 }
416
417 /**
418 * @internal
419 */
_destroyefl::eina::optional420 void _destroy()
421 {
422 if(is_engaged())
423 {
424 static_cast<T*>(static_cast<void*>(&buffer))->~T();
425 engaged = false;
426 }
427 }
428
429 typedef typename std::aligned_storage
430 <sizeof(T),std::alignment_of<T>::value>::type buffer_type;
431
432 /**
433 * Member variable for holding the contained value.
434 */
435 buffer_type buffer;
436
437 /**
438 * Flag to tell whether the object is engaged or not.
439 */
440 bool engaged;
441 };
442
443 template <typename T>
444 struct optional<T&>
445 {
446 typedef optional<T&> _self_type; /**< Type for the optional class itself. */
447
448 /**
449 * @brief Create a disengaged object.
450 *
451 * This constructor creates a disengaged <tt>eina::optional</tt>
452 * object.
453 */
optionalefl::eina::optional454 constexpr optional(std::nullptr_t) : pointer(nullptr)
455 {}
456
457 /**
458 * @brief Default constructor. Create a disengaged object.
459 */
optionalefl::eina::optional460 constexpr optional() : pointer(nullptr)
461 {}
462
463 /**
464 * @brief Create an engaged object by moving @p other content.
465 * @param other R-value reference to the desired type.
466 *
467 * This constructor creates an <tt>eina::optional</tt> object in an
468 * engaged state. The contained value is initialized by moving
469 * @p other.
470 */
optionalefl::eina::optional471 optional(T& other) : pointer(&other)
472 {
473 }
474
475 /**
476 * @brief Copy constructor. Create an object containing the same value as @p other and in the same state.
477 * @param other Constant reference to another <tt>eina::optional</tt> object that holds the same value type.
478 *
479 * This constructor creates an <tt>eina::optional</tt> object with
480 * the same engagement state of @p other. If @p other is engaged then
481 * the contained value of the newly created object is initialized by
482 * copying the contained value of @p other.
483 */
484 optional(_self_type const& other) = default;
485
486 /**
487 * @brief Assign new content to the object.
488 * @param other Constant reference to another <tt>eina::optional</tt> object that holds the same value type.
489 *
490 * This operator replaces the current content of the object. If
491 * @p other is engaged its contained value is copied to this object,
492 * making <tt>*this</tt> be considered engaged too. If @p other is
493 * disengaged <tt>*this</tt> is also made disengaged and its
494 * contained value, if any, is simple destroyed.
495 */
496 _self_type& operator=(_self_type const& other) = default;
497
498 /**
499 * @brief Disengage the object, destroying the current contained value, if any.
500 */
disengageefl::eina::optional501 void disengage()
502 {
503 pointer = NULL;
504 }
505
506 /**
507 * @brief Convert to @c bool based on whether the object is engaged or not.
508 * @return @c true if the object is engaged, @c false otherwise.
509 */
operator boolefl::eina::optional510 explicit operator bool() const
511 {
512 return pointer;
513 }
514
515 /**
516 * @brief Convert to @c bool based on whether the object is engaged or not.
517 * @return @c true if the object is disengaged, @c false otherwise.
518 */
operator !efl::eina::optional519 bool operator!() const
520 {
521 bool b ( *this );
522 return !b;
523 }
524
525 /**
526 * @brief Access member of the contained value.
527 * @return Pointer to the contained value, whose member will be accessed.
528 */
operator ->efl::eina::optional529 T* operator->()
530 {
531 assert(is_engaged());
532 return pointer;
533 }
534
535 /**
536 * @brief Access constant member of the contained value.
537 * @return Constant pointer to the contained value, whose member will be accessed.
538 */
operator ->efl::eina::optional539 T const* operator->() const
540 {
541 return pointer;
542 }
543
544 /**
545 * @brief Get the contained value.
546 * @return Reference to the contained value.
547 */
operator *efl::eina::optional548 T& operator*() { return get(); }
549
550 /**
551 * @brief Get the contained value.
552 * @return Constant reference to the contained value.
553 */
operator *efl::eina::optional554 T const& operator*() const { return get(); }
555
556 /**
557 * @brief Get the contained value.
558 * @return Reference to the contained value.
559 */
getefl::eina::optional560 T& get() { return *this->operator->(); }
561
562 /**
563 * @brief Get the contained value.
564 * @return Constant reference to the contained value.
565 */
getefl::eina::optional566 T const& get() const { return *this->operator->(); }
567
568 /**
569 * @brief Swap content with another <tt>eina::optional</tt> object.
570 * @param other Another <tt>eina::optional</tt> object.
571 */
swapefl::eina::optional572 void swap(optional<T>& other)
573 {
574 std::swap(pointer, other.pointer);
575 }
576
577 /**
578 * @brief Check if the object is engaged.
579 * @return @c true if the object is currently engaged, @c false otherwise.
580 */
is_engagedefl::eina::optional581 bool is_engaged() const
582 {
583 return pointer;
584 }
585 private:
586
587 /**
588 * Member variable for holding the contained value.
589 */
590 T* pointer;
591 };
592
593 template <typename T>
594 constexpr optional<typename std::decay<T>::type>
make_optional(T && value)595 make_optional(T&& value)
596 {
597 return optional<typename std::decay<T>::type>(std::forward<T>(value));
598 }
599
600 /**
601 * @brief Swap content with another <tt>eina::optional</tt> object.
602 *
603 */
604 template <typename T>
swap(optional<T> & lhs,optional<T> & rhs)605 void swap(optional<T>& lhs, optional<T>& rhs)
606 {
607 lhs.swap(rhs);
608 }
609
610 /**
611 * @brief Check if both <tt>eina::optional</tt> object are equal.
612 * @param lhs <tt>eina::optional</tt> object at the left side of the expression.
613 * @param rhs <tt>eina::optional</tt> object at the right side of the expression.
614 * @return @c true if both are objects are disengaged of if both objects
615 * are engaged and contain the same value, @c false in all
616 * other cases.
617 */
618 template <typename T>
operator ==(optional<T> const & lhs,optional<T> const & rhs)619 bool operator==(optional<T> const& lhs, optional<T> const& rhs)
620 {
621 if(!lhs && !rhs)
622 return true;
623 else if(!lhs || !rhs)
624 return false;
625 else
626 return *lhs == *rhs;
627 }
628
629 /**
630 * @brief Check if the <tt>eina::optional</tt> objects are different.
631 * @param lhs <tt>eina::optional</tt> object at the left side of the expression.
632 * @param rhs <tt>eina::optional</tt> object at the right side of the expression.
633 * @return The opposite of @ref operator==(optional<T> const& lhs, optional<T> const& rhs).
634 */
635 template <typename T>
operator !=(optional<T> const & lhs,optional<T> const & rhs)636 bool operator!=(optional<T> const& lhs, optional<T> const& rhs)
637 {
638 return !(lhs == rhs);
639 }
640
641 /**
642 * @brief Less than comparison between <tt>eina::optional</tt> objects.
643 * @param lhs <tt>eina::optional</tt> object at the left side of the expression.
644 * @param rhs <tt>eina::optional</tt> object at the right side of the expression.
645 * @return @c true if both objects are engaged and the contained value
646 * of @p lhs is less than the contained value of @p rhs, or if
647 * only @p lhs is disengaged. In all other cases returns
648 * @c false.
649 */
650 template <typename T>
operator <(optional<T> const & lhs,optional<T> const & rhs)651 bool operator<(optional<T> const& lhs, optional<T> const& rhs)
652 {
653 if(!lhs && !rhs)
654 return false;
655 else if(!lhs)
656 return true;
657 else if(!rhs)
658 return false;
659 else
660 return *lhs < *rhs;
661 }
662
663 /**
664 * @brief Less than or equal comparison between <tt>eina::optional</tt> objects.
665 * @param lhs <tt>eina::optional</tt> object at the left side of the expression.
666 * @param rhs <tt>eina::optional</tt> object at the right side of the expression.
667 * @return @c true if @p lhs is disengaged or if both objects are
668 * engaged and the contained value of @p lhs is less than or
669 * equal to the contained value of @p rhs. In all other cases
670 * returns @c false.
671 */
672 template <typename T>
operator <=(optional<T> const & lhs,optional<T> const & rhs)673 bool operator<=(optional<T> const& lhs, optional<T> const& rhs)
674 {
675 return lhs < rhs || lhs == rhs;
676 }
677
678 /**
679 * @brief More than comparison between <tt>eina::optional</tt> objects.
680 * @param lhs <tt>eina::optional</tt> object at the left side of the expression.
681 * @param rhs <tt>eina::optional</tt> object at the right side of the expression.
682 * @return @c true if both objects are engaged and the contained value
683 * of @p lhs is more than the contained value of @p rhs, or if
684 * only @p rhs is disengaged. In all other cases returns
685 * @c false.
686 */
687 template <typename T>
operator >(optional<T> const & lhs,optional<T> const & rhs)688 bool operator>(optional<T> const& lhs, optional<T> const& rhs)
689 {
690 return !(lhs <= rhs);
691 }
692
693 /**
694 * @brief More than or equal comparison between <tt>eina::optional</tt> objects.
695 * @param lhs <tt>eina::optional</tt> object at the left side of the expression.
696 * @param rhs <tt>eina::optional</tt> object at the right side of the expression.
697 * @return @c true if @p rhs is disengaged or if both objects are
698 * engaged and the contained value of @p lhs is more than or
699 * equal to the contained value of @p rhs. In all other
700 * cases returns @c false.
701 */
702 template <typename T>
operator >=(optional<T> const & lhs,optional<T> const & rhs)703 bool operator>=(optional<T> const& lhs, optional<T> const& rhs)
704 {
705 return !(lhs < rhs);
706 }
707
708 /**
709 * @}
710 */
711
712 } } // efl::eina
713
714 /**
715 * @}
716 */
717
718 #endif // EINA_OPTIONAL_HH_
719