[v3 PATCH] Cross-port exception-safety and move fixes of std::any to std::experimental::any.

Ville Voutilainen ville.voutilainen@gmail.com
Sun Oct 23 23:33:00 GMT 2016


Tested on Linux-x64. Ok for trunk?

I don't plan to backport this unless somebody shouts.

2016-10-24  Ville Voutilainen  <ville.voutilainen@gmail.com>

    Cross-port exception-safety and move fixes of std::any to
    std::experimental::any.
    * include/std/any (operator=(const any&)):
    Make strongly exception-safe.
    (operator=(any&&)): clear() unconditionally in the case where
    rhs has a value.
    (_Manager_internal<_Tp>::_S_manage): Move in _Op_xfer, don't copy.
    * testsuite/experimental/any/assign/2.cc: Adjust.
    * testsuite/experimental/any/assign/exception.cc: New.
    * testsuite/experimental/any/cons/2.cc: Adjust.
    * testsuite/experimental/any/misc/any_cast_neg.cc: Ajust.
-------------- next part --------------
diff --git a/libstdc++-v3/include/experimental/any b/libstdc++-v3/include/experimental/any
index 5e091a4..8fd66e2 100644
--- a/libstdc++-v3/include/experimental/any
+++ b/libstdc++-v3/include/experimental/any
@@ -191,16 +191,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     /// Copy the state of another object.
     any& operator=(const any& __rhs)
     {
-      if (__rhs.empty())
-	clear();
-      else if (this != &__rhs)
-	{
-	  if (!empty())
-	    _M_manager(_Op_destroy, this, nullptr);
-	  _Arg __arg;
-	  __arg._M_any = this;
-	  __rhs._M_manager(_Op_clone, &__rhs, &__arg);
-	}
+      *this = any(__rhs);
       return *this;
     }
 
@@ -215,8 +206,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	clear();
       else if (this != &__rhs)
 	{
-	  if (!empty())
-	    _M_manager(_Op_destroy, this, nullptr);
+	  clear();
 	  _Arg __arg;
 	  __arg._M_any = this;
 	  __rhs._M_manager(_Op_xfer, &__rhs, &__arg);
@@ -485,7 +475,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__ptr->~_Tp();
 	break;
       case _Op_xfer:
-	::new(&__arg->_M_any->_M_storage._M_buffer) _Tp(*__ptr);
+	::new(&__arg->_M_any->_M_storage._M_buffer) _Tp
+	  (std::move(*const_cast<_Tp*>(__ptr)));
 	__ptr->~_Tp();
 	__arg->_M_any->_M_manager = __any->_M_manager;
 	const_cast<any*>(__any)->_M_manager = nullptr;
diff --git a/libstdc++-v3/testsuite/experimental/any/assign/2.cc b/libstdc++-v3/testsuite/experimental/any/assign/2.cc
index 7022878..0232af6 100644
--- a/libstdc++-v3/testsuite/experimental/any/assign/2.cc
+++ b/libstdc++-v3/testsuite/experimental/any/assign/2.cc
@@ -23,28 +23,70 @@
 using std::experimental::any;
 using std::experimental::any_cast;
 
+bool moved = false;
+bool copied = false;
+
+
 struct X
 {
-  bool moved = false;
-  bool moved_from = false;
   X() = default;
-  X(const X&) = default;
-  X(X&& x) : moved(true) { x.moved_from = true; }
+  X(const X&) { copied = true; }
+  X(X&& x) { moved = true; }
+};
+
+struct X2
+{
+  X2() = default;
+  X2(const X2&) { copied = true; }
+  X2(X2&& x) noexcept { moved = true; }
 };
 
 void test01()
 {
+  moved = false;
   X x;
   any a1;
   a1 = x;
-  VERIFY(x.moved_from == false);
+  VERIFY(moved == false);
   any a2;
+  copied = false;
   a2 = std::move(x);
-  VERIFY(x.moved_from == true);
-  VERIFY(any_cast<X&>(a2).moved == true );
+  VERIFY(moved == true);
+  VERIFY(copied == false);
 }
 
+void test02()
+{
+  moved = false;
+  X x;
+  any a1;
+  a1 = x;
+  VERIFY(moved == false);
+  any a2;
+  copied = false;
+  a2 = std::move(a1);
+  VERIFY(moved == false);
+  VERIFY(copied == false);
+}
+
+void test03()
+{
+  moved = false;
+  X2 x;
+  any a1;
+  a1 = x;
+  VERIFY(copied && moved);
+  any a2;
+  moved = false;
+  copied = false;
+  a2 = std::move(a1);
+  VERIFY(moved == true);
+  VERIFY(copied == false);
+ }
+
 int main()
 {
   test01();
+  test02();
+  test03();
 }
diff --git a/libstdc++-v3/testsuite/experimental/any/assign/exception.cc b/libstdc++-v3/testsuite/experimental/any/assign/exception.cc
new file mode 100644
index 0000000..f125213
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/any/assign/exception.cc
@@ -0,0 +1,77 @@
+// { dg-do run { target c++14 } }
+
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <experimental/any>
+#include <testsuite_hooks.h>
+
+using std::experimental::any;
+using std::experimental::any_cast;
+
+bool should_throw = false;
+struct Bad
+{
+  Bad() = default;
+  Bad(const Bad&) {if (should_throw) throw 666;}
+};
+
+struct Bad2
+{
+  Bad2() = default;
+  Bad2(const Bad2&) {if (should_throw) throw 666;}
+  Bad2(Bad2&&) noexcept {}
+};
+
+int del_count = 0;
+struct Good 
+{
+  Good() = default;
+  Good(const Good&) = default;
+  Good(Good&&) = default;
+  ~Good() {++del_count;}
+};
+
+int main()
+{
+    any a1 = Good();
+    del_count = 0;
+    try {
+        Bad b;
+        any a2 = b;
+        should_throw = true;
+        a1 = a2;
+    } catch (...) {
+        auto x = any_cast<Good>(a1);
+        VERIFY( del_count == 0 );
+        VERIFY( !a1.empty() );
+        any_cast<Good>(a1);
+    }
+    any a3 = Good();
+    del_count = 0;
+    try {
+        Bad2 b;
+        any a4 = b;
+        should_throw = true;
+        a3 = a4;
+    } catch (...) {
+        auto x = any_cast<Good>(a1);
+        VERIFY( del_count == 0 );
+        VERIFY( !a1.empty() );
+        any_cast<Good>(a1);
+    }
+}
diff --git a/libstdc++-v3/testsuite/experimental/any/cons/2.cc b/libstdc++-v3/testsuite/experimental/any/cons/2.cc
index fef713d..c79573b 100644
--- a/libstdc++-v3/testsuite/experimental/any/cons/2.cc
+++ b/libstdc++-v3/testsuite/experimental/any/cons/2.cc
@@ -23,26 +23,59 @@
 using std::experimental::any;
 using std::experimental::any_cast;
 
+bool moved = false;
+bool copied = false;
+
 struct X
 {
-  bool moved = false;
-  bool moved_from = false;
   X() = default;
-  X(const X&) = default;
-  X(X&& x) : moved(true) { x.moved_from = true; }
+  X(const X&) { copied = true; }
+  X(X&& x) { moved = true; }
+};
+
+struct X2
+{
+  X2() = default;
+  X2(const X2&) { copied = true; }
+  X2(X2&& x) noexcept { moved = true; }
 };
 
 void test01()
 {
+  moved = false;
   X x;
   any a1(x);
-  VERIFY(x.moved_from == false);
+  VERIFY(moved == false);
   any a2(std::move(x));
-  VERIFY(x.moved_from == true);
-  VERIFY(any_cast<X&>(a2).moved == true );
+  VERIFY(moved == true);
+}
+
+void test02()
+{
+  moved = false;
+  X x;
+  any a1(x);
+  VERIFY(moved == false);
+  copied = false;
+  any a2(std::move(a1));
+  VERIFY(copied == false);
+}
+
+void test03()
+{
+  moved = false;
+  X2 x;
+  any a1(x);
+  VERIFY(moved == false);
+  copied = false;
+  any a2(std::move(a1));
+  VERIFY(copied == false);
+  VERIFY(moved == true);
 }
 
 int main()
 {
   test01();
+  test02();
+  test03();
 }
diff --git a/libstdc++-v3/testsuite/experimental/any/misc/any_cast_neg.cc b/libstdc++-v3/testsuite/experimental/any/misc/any_cast_neg.cc
index 4310572..924e798 100644
--- a/libstdc++-v3/testsuite/experimental/any/misc/any_cast_neg.cc
+++ b/libstdc++-v3/testsuite/experimental/any/misc/any_cast_neg.cc
@@ -25,5 +25,5 @@ void test01()
   using std::experimental::any_cast;
 
   const any y(1);
-  any_cast<int&>(y); // { dg-error "qualifiers" "" { target { *-*-* } } 369 }
+  any_cast<int&>(y); // { dg-error "qualifiers" "" { target { *-*-* } } 359 }
 }


More information about the Gcc-patches mailing list