diff --git a/src/bindings/eina_cxx/eina_optional.hh b/src/bindings/eina_cxx/eina_optional.hh index 64e42d7d8d..7cb2075e0a 100644 --- a/src/bindings/eina_cxx/eina_optional.hh +++ b/src/bindings/eina_cxx/eina_optional.hh @@ -66,15 +66,15 @@ struct optional * @brief Create a disengaged object. * * This constructor creates a disengaged eina::optional - * object, since null pointer is meant to be a valid object type. + * object. */ - optional(std::nullptr_t) : engaged(false) + constexpr optional(std::nullptr_t) : engaged(false) {} /** * @brief Default constructor. Create a disengaged object. */ - optional() : engaged(false) + constexpr optional() : engaged(false) {} /** @@ -158,10 +158,43 @@ struct optional optional(optional&& other) : engaged(false) { - _construct(std::move(*other)); + 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. @@ -175,8 +208,7 @@ struct optional _self_type& operator=(optional&& other) { _destroy(); - engaged = other.engaged; - if(engaged) + if (other.engaged) _construct(std::move(*other)); other._destroy(); return *this; @@ -199,6 +231,53 @@ struct optional 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. */ @@ -308,7 +387,7 @@ private: void _construct(U&& object) { assert(!is_engaged()); - new (&buffer) T(std::move(object)); + new (&buffer) T(std::forward(object)); engaged = true; } @@ -338,6 +417,13 @@ private: bool engaged; }; +template +constexpr optional::type> +make_optional(T&& value) +{ + return optional::type>(std::forward(value)); +} + /** * @brief Swap content with another eina::optional object. * diff --git a/src/tests/eina_cxx/eina_cxx_test_optional.cc b/src/tests/eina_cxx/eina_cxx_test_optional.cc index 6619ee96ba..4043cb064c 100644 --- a/src/tests/eina_cxx/eina_cxx_test_optional.cc +++ b/src/tests/eina_cxx/eina_cxx_test_optional.cc @@ -137,10 +137,64 @@ START_TEST(eina_cxx_optional_assignment) } END_TEST +START_TEST(eina_cxx_optional_convertible_types) +{ + namespace eina = efl::eina; + + eina::eina_init init; + + eina::optional a(1.0); + eina::optional b("2"); + eina::optional c(eina::string_view("3")); + + ck_assert(!!a && !!b && !!c); + + eina::optional a_s(a); + eina::optional b_s(b); + eina::optional c_s(c); + + ck_assert(!!a_s && !!b_s && !!c_s); + + fail_if(1.0 != *a_s); + fail_if(std::string("2") != *b_s); + fail_if(eina::string_view("3") != *c_s); + + fail_if(1 != *a); + fail_if("2" != *b); + fail_if("3" != *c); + + fail_if(*a != *a_s); + fail_if(*b != *b_s); + fail_if(*c != *c_s); + + a_s = 4; + b_s = "5"; + c_s = "6"; + + a = a_s; + b = b_s; + c = c_s; + + fail_if(*a != *a_s); + fail_if(*b != *b_s); + fail_if(*c != *c_s); + + a = *a_s; + b = *b_s; + c = *c_s; + + fail_if(*a != *a_s); + fail_if(*b != *b_s); + fail_if(*c != *c_s); +} +END_TEST + + void eina_test_optional(TCase* tc) { tcase_add_test(tc, eina_cxx_optional_constructors); tcase_add_test(tc, eina_cxx_optional_rel_ops); tcase_add_test(tc, eina_cxx_optional_assignment); + tcase_add_test(tc, eina_cxx_optional_convertible_types); }