eina_cxx: Fix compatibility between eina::optionals of convertible types

Now an eina::optional that wraps a specific type can be constructed or
have content assigned to it using another eina::optional that wraps a
different type, provided that the two wrapped types are convertible
between each other.

Added "disengage" member function to be able to disengage a R-value
eina::optional of different contained type.
It also adds for increased convenience.

Fix constructing an engaged eina::optional from an disengaged one.

Fix small assertion problem of trying to construct an eina::optional
already flagged as engaged.

Fix incorrect use of std::move. Changed it to std::forward.

Added constexpr property for trivial constructors.

Added auxiliary function "make_optional".

Added unit test to check compatibility between eina::optionals of
convertible types.
This commit is contained in:
Vitor Sousa 2015-02-12 23:22:50 -02:00 committed by Felipe Magno de Almeida
parent 5043dcb830
commit 5619c6bc8d
2 changed files with 147 additions and 7 deletions

View File

@ -66,15 +66,15 @@ struct optional
* @brief Create a disengaged object.
*
* This constructor creates a disengaged <tt>eina::optional</tt>
* 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<T>&& 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 <tt>eina::optional</tt> object that holds a different, but convertible, value type.
*
* This constructor creates an <tt>eina::optional</tt> 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 <typename U>
optional(optional<U>&& other, typename std::enable_if<std::is_convertible<U, T>::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 <tt>eina::optional</tt> object that holds a different, but convertible, value type.
*
* This constructor creates an <tt>eina::optional</tt> 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 <typename U>
optional(optional<U> const& other, typename std::enable_if<std::is_convertible<U, T>::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 <tt>eina::optional</tt> object that holds the same value type.
@ -175,8 +208,7 @@ struct optional
_self_type& operator=(optional<T>&& 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 <tt>eina::optional</tt> 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 <tt>*this</tt> be considered engaged too. If @p other is
* disengaged <tt>*this</tt> is also made disengaged and its
* contained value, if any, is simple destroyed.
*/
template <typename U>
typename std::enable_if<std::is_convertible<U, T>::value, _self_type>::type& operator=(optional<U>&& 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 <tt>eina::optional</tt> 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 <tt>*this</tt> be considered engaged too. If @p other is
* disengaged <tt>*this</tt> is also made disengaged and its
* contained value, if any, is simple destroyed.
*/
template <typename U>
typename std::enable_if<std::is_convertible<U, T>::value, _self_type>::type& operator=(optional<U>const& 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<U>(object));
engaged = true;
}
@ -338,6 +417,13 @@ private:
bool engaged;
};
template <typename T>
constexpr optional<typename std::decay<T>::type>
make_optional(T&& value)
{
return optional<typename std::decay<T>::type>(std::forward<T>(value));
}
/**
* @brief Swap content with another <tt>eina::optional</tt> object.
*

View File

@ -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<int> a(1.0);
eina::optional<eina::string_view> b("2");
eina::optional<std::string> c(eina::string_view("3"));
ck_assert(!!a && !!b && !!c);
eina::optional<double> a_s(a);
eina::optional<std::string> b_s(b);
eina::optional<eina::string_view> 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);
}