diff mbox

libstdc++/56785 reduce space overhead of nested tuples

Message ID 20150117002350.GN3360@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely Jan. 17, 2015, 12:23 a.m. UTC
This replaces the current empty _Tuple_impl that terminates the
recursive inheritance hierarchy, instead adding the extra code to the
last base class that holds data so that the recursion terminates there
instead.

The purpose of this is to avoid nested tuples having two instances of
the same _Tuple_impl<N> base class, which cannot be placed at the same
address and so take up space despite being empty.

Tested x86_64-linux, committed to trunk.

Comments

H.J. Lu Jan. 20, 2015, 5:09 p.m. UTC | #1
On Fri, Jan 16, 2015 at 4:23 PM, Jonathan Wakely <jwakely@redhat.com> wrote:
> This replaces the current empty _Tuple_impl that terminates the
> recursive inheritance hierarchy, instead adding the extra code to the
> last base class that holds data so that the recursion terminates there
> instead.
>
> The purpose of this is to avoid nested tuples having two instances of
> the same _Tuple_impl<N> base class, which cannot be placed at the same
> address and so take up space despite being empty.
>
> Tested x86_64-linux, committed to trunk.

This may have caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64695
diff mbox

Patch

commit 65e06eb5b8ee42fb024307538380f8a375aba7ca
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Jun 23 23:41:08 2014 +0100

    	PR libstdc++/56785
    	* include/std/tuple (_Tuple_impl): Remove zero-element specialization
    	and define one-element specialization.
    	* testsuite/20_util/tuple/56785.cc: New.

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index b710049..e500a76 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -158,30 +158,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<std::size_t _Idx, typename... _Elements>
     struct _Tuple_impl; 
 
-  /**
-   * Zero-element tuple implementation. This is the basis case for the 
-   * inheritance recursion.
-   */
-  template<std::size_t _Idx>
-    struct _Tuple_impl<_Idx>
-    {
-      template<std::size_t, typename...> friend class _Tuple_impl;
-
-      _Tuple_impl() = default;
-
-      template<typename _Alloc>
-        _Tuple_impl(allocator_arg_t, const _Alloc&) { }
-
-      template<typename _Alloc>
-        _Tuple_impl(allocator_arg_t, const _Alloc&, const _Tuple_impl&) { }
-
-      template<typename _Alloc>
-        _Tuple_impl(allocator_arg_t, const _Alloc&, _Tuple_impl&&) { }
-
-    protected:
-      void _M_swap(_Tuple_impl&) noexcept { /* no-op */ }
-    };
-
   template<typename _Tp>
     struct __is_empty_non_tuple : is_empty<_Tp> { };
 
@@ -358,6 +334,130 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     };
 
+  // Basis case of inheritance recursion.
+  template<std::size_t _Idx, typename _Head>
+    struct _Tuple_impl<_Idx, _Head>
+    : private _Head_base<_Idx, _Head, __empty_not_final<_Head>::value>
+    {
+      template<std::size_t, typename...> friend class _Tuple_impl;
+
+      typedef _Head_base<_Idx, _Head, __empty_not_final<_Head>::value> _Base;
+
+      static constexpr _Head&
+      _M_head(_Tuple_impl& __t) noexcept { return _Base::_M_head(__t); }
+
+      static constexpr const _Head&
+      _M_head(const _Tuple_impl& __t) noexcept { return _Base::_M_head(__t); }
+
+      constexpr _Tuple_impl()
+      : _Base() { }
+
+      explicit
+      constexpr _Tuple_impl(const _Head& __head)
+      : _Base(__head) { }
+
+      template<typename _UHead>
+        explicit
+        constexpr _Tuple_impl(_UHead&& __head)
+	: _Base(std::forward<_UHead>(__head)) { }
+
+      constexpr _Tuple_impl(const _Tuple_impl&) = default;
+
+      constexpr
+      _Tuple_impl(_Tuple_impl&& __in)
+      noexcept(is_nothrow_move_constructible<_Head>::value)
+      : _Base(std::forward<_Head>(_M_head(__in))) { }
+
+      template<typename _UHead>
+        constexpr _Tuple_impl(const _Tuple_impl<_Idx, _UHead>& __in)
+	: _Base(_Tuple_impl<_Idx, _UHead>::_M_head(__in)) { }
+
+      template<typename _UHead>
+        constexpr _Tuple_impl(_Tuple_impl<_Idx, _UHead>&& __in)
+	: _Base(std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+	{ }
+
+      template<typename _Alloc>
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
+	: _Base(__tag, __use_alloc<_Head>(__a)) { }
+
+      template<typename _Alloc>
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+		    const _Head& __head)
+	: _Base(__use_alloc<_Head, _Alloc, _Head>(__a), __head) { }
+
+      template<typename _Alloc, typename _UHead>
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+	            _UHead&& __head)
+	: _Base(__use_alloc<_Head, _Alloc, _UHead>(__a),
+	        std::forward<_UHead>(__head)) { }
+
+      template<typename _Alloc>
+        _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+	            const _Tuple_impl& __in)
+	: _Base(__use_alloc<_Head, _Alloc, _Head>(__a), _M_head(__in)) { }
+
+      template<typename _Alloc>
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+	            _Tuple_impl&& __in)
+	: _Base(__use_alloc<_Head, _Alloc, _Head>(__a),
+	        std::forward<_Head>(_M_head(__in))) { }
+
+      template<typename _Alloc, typename _UHead>
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+	            const _Tuple_impl<_Idx, _UHead>& __in)
+	: _Base(__use_alloc<_Head, _Alloc, _Head>(__a),
+		_Tuple_impl<_Idx, _UHead>::_M_head(__in)) { }
+
+      template<typename _Alloc, typename _UHead>
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+	            _Tuple_impl<_Idx, _UHead>&& __in)
+	: _Base(__use_alloc<_Head, _Alloc, _UHead>(__a),
+                std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+	{ }
+
+      _Tuple_impl&
+      operator=(const _Tuple_impl& __in)
+      {
+	_M_head(*this) = _M_head(__in);
+	return *this;
+      }
+
+      _Tuple_impl&
+      operator=(_Tuple_impl&& __in)
+      noexcept(is_nothrow_move_assignable<_Head>::value)
+      {
+	_M_head(*this) = std::forward<_Head>(_M_head(__in));
+	return *this;
+      }
+
+      template<typename _UHead>
+        _Tuple_impl&
+        operator=(const _Tuple_impl<_Idx, _UHead>& __in)
+        {
+	  _M_head(*this) = _Tuple_impl<_Idx, _UHead>::_M_head(__in);
+	  return *this;
+	}
+
+      template<typename _UHead>
+        _Tuple_impl&
+        operator=(_Tuple_impl<_Idx, _UHead>&& __in)
+        {
+	  _M_head(*this)
+	    = std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
+	  return *this;
+	}
+
+    protected:
+      void
+      _M_swap(_Tuple_impl& __in)
+      noexcept(noexcept(swap(std::declval<_Head&>(), std::declval<_Head&>())))
+      {
+	using std::swap;
+	swap(_M_head(*this), _M_head(__in));
+      }
+    };
+
   /// Primary class template, tuple
   template<typename... _Elements> 
     class tuple : public _Tuple_impl<0, _Elements...>
diff --git a/libstdc++-v3/testsuite/20_util/tuple/56785.cc b/libstdc++-v3/testsuite/20_util/tuple/56785.cc
new file mode 100644
index 0000000..504ab0a
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/56785.cc
@@ -0,0 +1,32 @@ 
+// Copyright (C) 2015 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/>.
+
+// { dg-options "-std=gnu++11" }
+// { dg-do compile }
+
+#include <tuple>
+
+class Empty { };
+
+using std::tuple;
+using char_pair = tuple<char, char>;
+
+static_assert( sizeof(tuple<Empty, char_pair>) == sizeof(char_pair),
+               "Nested tuple tuple<Empty, tuple<T,T>> is too big");
+
+static_assert( sizeof(tuple<char_pair, char_pair>) == (2 * sizeof(char_pair)),
+               "Nested tuple<tuple<T,T, tuple<T,T>> is too big" );