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