forked from enlightenment/efl
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:
parent
5043dcb830
commit
5619c6bc8d
|
@ -66,15 +66,15 @@ struct optional
|
||||||
* @brief Create a disengaged object.
|
* @brief Create a disengaged object.
|
||||||
*
|
*
|
||||||
* This constructor creates a disengaged <tt>eina::optional</tt>
|
* 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.
|
* @brief Default constructor. Create a disengaged object.
|
||||||
*/
|
*/
|
||||||
optional() : engaged(false)
|
constexpr optional() : engaged(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,10 +158,43 @@ struct optional
|
||||||
optional(optional<T>&& other)
|
optional(optional<T>&& other)
|
||||||
: engaged(false)
|
: engaged(false)
|
||||||
{
|
{
|
||||||
_construct(std::move(*other));
|
if(other.engaged) _construct(std::move(*other));
|
||||||
other._destroy();
|
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.
|
* @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.
|
* @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)
|
_self_type& operator=(optional<T>&& other)
|
||||||
{
|
{
|
||||||
_destroy();
|
_destroy();
|
||||||
engaged = other.engaged;
|
if (other.engaged)
|
||||||
if(engaged)
|
|
||||||
_construct(std::move(*other));
|
_construct(std::move(*other));
|
||||||
other._destroy();
|
other._destroy();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -199,6 +231,53 @@ struct optional
|
||||||
return *this;
|
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.
|
* @brief Releases the contained value if the object is engaged.
|
||||||
*/
|
*/
|
||||||
|
@ -308,7 +387,7 @@ private:
|
||||||
void _construct(U&& object)
|
void _construct(U&& object)
|
||||||
{
|
{
|
||||||
assert(!is_engaged());
|
assert(!is_engaged());
|
||||||
new (&buffer) T(std::move(object));
|
new (&buffer) T(std::forward<U>(object));
|
||||||
engaged = true;
|
engaged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,6 +417,13 @@ private:
|
||||||
bool engaged;
|
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.
|
* @brief Swap content with another <tt>eina::optional</tt> object.
|
||||||
*
|
*
|
||||||
|
|
|
@ -137,10 +137,64 @@ START_TEST(eina_cxx_optional_assignment)
|
||||||
}
|
}
|
||||||
END_TEST
|
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
|
void
|
||||||
eina_test_optional(TCase* tc)
|
eina_test_optional(TCase* tc)
|
||||||
{
|
{
|
||||||
tcase_add_test(tc, eina_cxx_optional_constructors);
|
tcase_add_test(tc, eina_cxx_optional_constructors);
|
||||||
tcase_add_test(tc, eina_cxx_optional_rel_ops);
|
tcase_add_test(tc, eina_cxx_optional_rel_ops);
|
||||||
tcase_add_test(tc, eina_cxx_optional_assignment);
|
tcase_add_test(tc, eina_cxx_optional_assignment);
|
||||||
|
tcase_add_test(tc, eina_cxx_optional_convertible_types);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue