eina_cxx: Fix compatibility between eina::optionals of convertible types
authorVitor Sousa <vitorsousasilva@gmail.com>
Fri, 13 Feb 2015 01:22:50 +0000 (23:22 -0200)
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>
Tue, 14 Apr 2015 04:06:57 +0000 (01:06 -0300)
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.

src/bindings/eina_cxx/eina_optional.hh
src/tests/eina_cxx/eina_cxx_test_optional.cc

index 64e42d7..7cb2075 100644 (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,11 +158,44 @@ 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;
@@ -200,6 +232,53 @@ struct optional
    }
 
    /**
+    * @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.
     */
    ~optional()
@@ -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.
  *
index 6619ee9..4043cb0 100644 (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);
 }