/* * Copyright 2019 by its authors. See AUTHORS. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef EINA_OPTIONAL_HH_ #define EINA_OPTIONAL_HH_ #include #include #include #include /** * @addtogroup Eina_Cxx_Data_Types_Group * * @{ */ namespace efl_eina_swap_adl { /** * @internal */ template void swap_impl(T& lhs, T& rhs) { using namespace std; swap(lhs, rhs); } } namespace efl { namespace eina { /** * @defgroup Eina_Cxx_Optional_Group Optional Value * @ingroup Eina_Cxx_Data_Types_Group * * @{ */ /** * @internal */ template void adl_swap(T& lhs, T& rhs) { ::efl_eina_swap_adl::swap_impl(lhs, rhs); } /** * This class manages an optional contained value, i.e. a value that * semantically may not be present. * * A common use case for optional is the return value of a function that * may fail. As opposed to other approaches, such as * std::pair, optional handles expensive to construct * objects well and is more readable, as the intent is expressed * explicitly. * * An optional object holding a semantically present value is considered * to be @em engaged, otherwise it is considered to be @em disengaged. */ template struct optional { typedef optional _self_type; /**< Type for the optional class itself. */ /** * @brief Create a disengaged object. * * This constructor creates a disengaged eina::optional * object. */ constexpr optional(std::nullptr_t) : engaged(false) {} /** * @brief Default constructor. Create a disengaged object. */ constexpr optional() : engaged(false) {} /** * @brief Create an engaged object by moving @p other content. * @param other R-value reference to the desired type. * * This constructor creates an eina::optional object in an * engaged state. The contained value is initialized by moving * @p other. */ optional(T&& other) : engaged(false) { _construct(std::forward(other)); } /** * @brief Create an engaged object by copying @p other content. * @param other Constant reference to the desired type. * * This constructor creates an eina::optional object in an * engaged state. The contained value is initialized by copying * @p other. */ optional(T const& other) : engaged(false) { _construct(other); } /** * @brief Create an engaged object by moving @p other content. * @param other R-value reference to the desired type. * * This constructor creates an eina::optional object in an * engaged state. The contained value is initialized by moving * @p other. */ template optional(U&& other, typename std::enable_if::value>::type* = 0) : engaged(false) { _construct(std::forward(other)); } /** * @brief Create an engaged object by copying @p other content. * @param other Constant reference to the desired type. * * This constructor creates an eina::optional object in an * engaged state. The contained value is initialized by copying * @p other. */ template optional(U const& other, typename std::enable_if::value>::type* = 0) : engaged(false) { _construct(other); } /** * @brief Copy constructor. Create an object containing the same value as @p other and in the same state. * @param other Constant reference to another eina::optional object that holds the same value type. * * This constructor creates an eina::optional object with * the same engagement state of @p other. If @p other is engaged then * the contained value of the newly created object is initialized by * copying the contained value of @p other. */ optional(optional const& other) : engaged(false) { if(other.engaged) _construct(*other); } /** * @brief Move constructor. Create an object containing the same value as @p other and in the same state. * @param other R-value reference to another eina::optional object that holds the same value type. * * This constructor creates an eina::optional object with * the same engagement state of @p other. If @p other is engaged then * the contained value of the newly created object is initialized by * moving the contained value of @p other. */ optional(optional&& other) : engaged(false) { if(other.engaged) _construct(std::move(*other)); other._destroy(); } /** * @brief Move constructor. Create an object containing the same value as @p other and in the same state. * @param other R-value reference to another eina::optional object that holds a different, but convertible, value type. * * This constructor creates an eina::optional object with * the same engagement state of @p other. If @p other is engaged then * the contained value of the newly created object is initialized by * moving the contained value of @p other. */ template optional(optional&& other, typename std::enable_if::value>::type* = 0) : engaged(false) { if (other.is_engaged()) _construct(std::move(*other)); other.disengage(); } /** * @brief Copy constructor. Create an object containing the same value as @p other and in the same state. * @param other Constant reference to another eina::optional object that holds a different, but convertible, value type. * * This constructor creates an eina::optional object with * the same engagement state of @p other. If @p other is engaged then * the contained value of the newly created object is initialized by * converting and copying the contained value of @p other. */ template optional(optional const& other, typename std::enable_if::value>::type* = 0) : engaged(false) { if (other.is_engaged()) _construct(*other); } /** * @brief Assign new content to the object. * @param other R-value reference to another eina::optional object that holds the same value type. * * This operator replaces the current content of the object. If * @p other is engaged its contained value is moved to this object, * making *this be considered engaged too. If @p other is * disengaged *this is also made disengaged and its * contained value, if any, is simple destroyed. */ _self_type& operator=(optional&& other) { _destroy(); if (other.engaged) _construct(std::move(*other)); other._destroy(); return *this; } /** * @brief Assign new content to the object. * @param other Constant reference to another eina::optional object that holds the same value type. * * This operator replaces the current content of the object. If * @p other is engaged its contained value is copied to this object, * making *this be considered engaged too. If @p other is * disengaged *this is also made disengaged and its * contained value, if any, is simple destroyed. */ _self_type& operator=(optionalconst& other) { optional tmp(other); tmp.swap(*this); return *this; } /** * @brief Assign new content to the object. * @param other R-value reference to another eina::optional object that holds a different, but convertible, value type. * * This operator replaces the current content of the object. If * @p other is engaged its contained value is moved to this object, * making *this be considered engaged too. If @p other is * disengaged *this is also made disengaged and its * contained value, if any, is simple destroyed. */ template typename std::enable_if::value, _self_type>::type& operator=(optional&& other) { _destroy(); if (other.is_engaged()) _construct(std::move(*other)); other.disengage(); return *this; } /** * @brief Assign new content to the object. * @param other Constant reference to another eina::optional object that holds a different, but convertible, value type. * * This operator replaces the current content of the object. If * @p other is engaged its contained value is converted and copied to this * object, making *this be considered engaged too. If @p other is * disengaged *this is also made disengaged and its * contained value, if any, is simple destroyed. */ template typename std::enable_if::value, _self_type>::type& operator=(optionalconst& other) { _destroy(); if (other.is_engaged()) _construct(*other); return *this; } /** * @brief Disengage the object, destroying the current contained value, if any. */ void disengage() { _destroy(); } /** * @brief Releases the contained value if the object is engaged. */ ~optional() { _destroy(); } /** * @brief Convert to @c bool based on whether the object is engaged or not. * @return @c true if the object is engaged, @c false otherwise. */ explicit operator bool() const { return is_engaged(); } /** * @brief Convert to @c bool based on whether the object is engaged or not. * @return @c true if the object is disengaged, @c false otherwise. */ bool operator!() const { bool b ( *this ); return !b; } /** * @brief Access member of the contained value. * @return Pointer to the contained value, whose member will be accessed. */ T* operator->() { assert(is_engaged()); return static_cast(static_cast(&buffer)); } /** * @brief Access constant member of the contained value. * @return Constant pointer to the contained value, whose member will be accessed. */ T const* operator->() const { return const_cast<_self_type&>(*this).operator->(); } /** * @brief Get the contained value. * @return Reference to the contained value. */ T& operator*() { return get(); } /** * @brief Get the contained value. * @return Constant reference to the contained value. */ T const& operator*() const { return get(); } /** * @brief Get the contained value. * @return Reference to the contained value. */ T& get() { return *this->operator->(); } /** * @brief Get the contained value. * @return Constant reference to the contained value. */ T const& get() const { return *this->operator->(); } /** * @brief Swap content with another eina::optional object. * @param other Another eina::optional object. */ void swap(optional& other) { if(is_engaged() && other.is_engaged()) { eina::adl_swap(**this, *other); } else if(is_engaged()) { other._construct(std::move(**this)); _destroy(); } else if(other.is_engaged()) { _construct(std::move(*other)); other._destroy(); } } /** * @brief Check if the object is engaged. * @return @c true if the object is currently engaged, @c false otherwise. */ bool is_engaged() const { return engaged; } private: /** * @internal */ template void _construct(U&& object) { assert(!is_engaged()); // NOTE: the buffer memory is intended to be in an // uninitialized state here. // So this warning can be disabled. #pragma GCC diagnostic push #ifndef __clang__ #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif new (&buffer) T(std::forward(object)); #pragma GCC diagnostic pop engaged = true; } /** * @internal */ void _destroy() { if(is_engaged()) { static_cast(static_cast(&buffer))->~T(); engaged = false; } } typedef typename std::aligned_storage ::value>::type buffer_type; /** * Member variable for holding the contained value. */ buffer_type buffer; /** * Flag to tell whether the object is engaged or not. */ bool engaged; }; template struct optional { typedef optional _self_type; /**< Type for the optional class itself. */ /** * @brief Create a disengaged object. * * This constructor creates a disengaged eina::optional * object. */ constexpr optional(std::nullptr_t) : pointer(nullptr) {} /** * @brief Default constructor. Create a disengaged object. */ constexpr optional() : pointer(nullptr) {} /** * @brief Create an engaged object by moving @p other content. * @param other R-value reference to the desired type. * * This constructor creates an eina::optional object in an * engaged state. The contained value is initialized by moving * @p other. */ optional(T& other) : pointer(&other) { } /** * @brief Copy constructor. Create an object containing the same value as @p other and in the same state. * @param other Constant reference to another eina::optional object that holds the same value type. * * This constructor creates an eina::optional object with * the same engagement state of @p other. If @p other is engaged then * the contained value of the newly created object is initialized by * copying the contained value of @p other. */ optional(_self_type const& other) = default; /** * @brief Assign new content to the object. * @param other Constant reference to another eina::optional object that holds the same value type. * * This operator replaces the current content of the object. If * @p other is engaged its contained value is copied to this object, * making *this be considered engaged too. If @p other is * disengaged *this is also made disengaged and its * contained value, if any, is simple destroyed. */ _self_type& operator=(_self_type const& other) = default; /** * @brief Disengage the object, destroying the current contained value, if any. */ void disengage() { pointer = NULL; } /** * @brief Convert to @c bool based on whether the object is engaged or not. * @return @c true if the object is engaged, @c false otherwise. */ explicit operator bool() const { return pointer; } /** * @brief Convert to @c bool based on whether the object is engaged or not. * @return @c true if the object is disengaged, @c false otherwise. */ bool operator!() const { bool b ( *this ); return !b; } /** * @brief Access member of the contained value. * @return Pointer to the contained value, whose member will be accessed. */ T* operator->() { assert(is_engaged()); return pointer; } /** * @brief Access constant member of the contained value. * @return Constant pointer to the contained value, whose member will be accessed. */ T const* operator->() const { return pointer; } /** * @brief Get the contained value. * @return Reference to the contained value. */ T& operator*() { return get(); } /** * @brief Get the contained value. * @return Constant reference to the contained value. */ T const& operator*() const { return get(); } /** * @brief Get the contained value. * @return Reference to the contained value. */ T& get() { return *this->operator->(); } /** * @brief Get the contained value. * @return Constant reference to the contained value. */ T const& get() const { return *this->operator->(); } /** * @brief Swap content with another eina::optional object. * @param other Another eina::optional object. */ void swap(optional& other) { std::swap(pointer, other.pointer); } /** * @brief Check if the object is engaged. * @return @c true if the object is currently engaged, @c false otherwise. */ bool is_engaged() const { return pointer; } private: /** * Member variable for holding the contained value. */ T* pointer; }; template constexpr optional::type> make_optional(T&& value) { return optional::type>(std::forward(value)); } /** * @brief Swap content with another eina::optional object. * */ template void swap(optional& lhs, optional& rhs) { lhs.swap(rhs); } /** * @brief Check if both eina::optional object are equal. * @param lhs eina::optional object at the left side of the expression. * @param rhs eina::optional object at the right side of the expression. * @return @c true if both are objects are disengaged of if both objects * are engaged and contain the same value, @c false in all * other cases. */ template bool operator==(optional const& lhs, optional const& rhs) { if(!lhs && !rhs) return true; else if(!lhs || !rhs) return false; else return *lhs == *rhs; } /** * @brief Check if the eina::optional objects are different. * @param lhs eina::optional object at the left side of the expression. * @param rhs eina::optional object at the right side of the expression. * @return The opposite of @ref operator==(optional const& lhs, optional const& rhs). */ template bool operator!=(optional const& lhs, optional const& rhs) { return !(lhs == rhs); } /** * @brief Less than comparison between eina::optional objects. * @param lhs eina::optional object at the left side of the expression. * @param rhs eina::optional object at the right side of the expression. * @return @c true if both objects are engaged and the contained value * of @p lhs is less than the contained value of @p rhs, or if * only @p lhs is disengaged. In all other cases returns * @c false. */ template bool operator<(optional const& lhs, optional const& rhs) { if(!lhs && !rhs) return false; else if(!lhs) return true; else if(!rhs) return false; else return *lhs < *rhs; } /** * @brief Less than or equal comparison between eina::optional objects. * @param lhs eina::optional object at the left side of the expression. * @param rhs eina::optional object at the right side of the expression. * @return @c true if @p lhs is disengaged or if both objects are * engaged and the contained value of @p lhs is less than or * equal to the contained value of @p rhs. In all other cases * returns @c false. */ template bool operator<=(optional const& lhs, optional const& rhs) { return lhs < rhs || lhs == rhs; } /** * @brief More than comparison between eina::optional objects. * @param lhs eina::optional object at the left side of the expression. * @param rhs eina::optional object at the right side of the expression. * @return @c true if both objects are engaged and the contained value * of @p lhs is more than the contained value of @p rhs, or if * only @p rhs is disengaged. In all other cases returns * @c false. */ template bool operator>(optional const& lhs, optional const& rhs) { return !(lhs <= rhs); } /** * @brief More than or equal comparison between eina::optional objects. * @param lhs eina::optional object at the left side of the expression. * @param rhs eina::optional object at the right side of the expression. * @return @c true if @p rhs is disengaged or if both objects are * engaged and the contained value of @p lhs is more than or * equal to the contained value of @p rhs. In all other * cases returns @c false. */ template bool operator>=(optional const& lhs, optional const& rhs) { return !(lhs < rhs); } /** * @} */ } } // efl::eina /** * @} */ #endif // EINA_OPTIONAL_HH_