public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-3441] libstdc++: Specialize ranges::__detail::__box for semiregular types
@ 2020-09-24 16:59 Patrick Palka
  0 siblings, 0 replies; only message in thread
From: Patrick Palka @ 2020-09-24 16:59 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:42907ca9a495a4a535bbd995fa126afb76012023

commit r11-3441-g42907ca9a495a4a535bbd995fa126afb76012023
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu Sep 24 12:58:39 2020 -0400

    libstdc++: Specialize ranges::__detail::__box for semiregular types
    
    The class template semiregular-box<T> defined in [range.semi.wrap] is
    used by a number of views to accomodate non-semiregular subobjects
    while ensuring that the overall view remains semiregular.  It provides
    a stand-in default constructor, copy assignment operator and move
    assignment operator whenever the underlying type lacks them.  The
    wrapper derives from std::optional<T> to support default construction
    when T is not default constructible.
    
    It would be nice for this wrapper to essentially be a no-op when the
    underlying type is already semiregular, but this is currently not the
    case due to its use of std::optional<T>, which incurs space overhead
    compared to storing just T.
    
    To that end, this patch specializes the semiregular wrapper for
    semiregular T.  Compared to the primary template, this specialization
    uses less space, and it allows [[no_unique_address]] to optimize away
    wrapped data members whose underlying type is empty and semiregular
    (e.g. a non-capturing lambda).  This patch also applies
    [[no_unique_address]] to the five data members that use the wrapper.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/ranges (__detail::__boxable): Split out the
            associated constraints of __box into here.
            (__detail::__box): Use the __boxable concept.  Define a leaner
            partial specialization for semiregular types.
            (single_view::_M_value): Give it [[no_unique_address]].
            (filter_view::_M_pred): Likewise.
            (transform_view::_M_fun): Likewise.
            (take_while_view::_M_pred): Likewise.
            (drop_while_view::_M_pred):: Likewise.
            * testsuite/std/ranges/adaptors/detail/semiregular_box.cc: New
            test.

Diff:
---
 libstdc++-v3/include/std/ranges                    | 68 ++++++++++++++++++--
 .../std/ranges/adaptors/detail/semiregular_box.cc  | 73 ++++++++++++++++++++++
 2 files changed, 135 insertions(+), 6 deletions(-)

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index e7fa4493612..ed04fa0001d 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -86,7 +86,10 @@ namespace ranges
 
   namespace __detail
   {
-    template<copy_constructible _Tp> requires is_object_v<_Tp>
+    template<typename _Tp>
+      concept __boxable = copy_constructible<_Tp> && is_object_v<_Tp>;
+
+    template<__boxable _Tp>
       struct __box : std::optional<_Tp>
       {
 	using std::optional<_Tp>::optional;
@@ -130,6 +133,59 @@ namespace ranges
 	}
       };
 
+    // For types which are already semiregular, this specialization of the
+    // semiregular wrapper stores the object directly without going through
+    // std::optional.  It provides just the subset of the primary template's
+    // API that we currently use.
+    template<__boxable _Tp> requires semiregular<_Tp>
+      struct __box<_Tp>
+      {
+      private:
+	[[no_unique_address]] _Tp _M_value;
+
+      public:
+	__box() = default;
+
+	constexpr explicit
+	__box(const _Tp& __t)
+	noexcept(is_nothrow_copy_constructible_v<_Tp>)
+	: _M_value{__t}
+	{ }
+
+	constexpr explicit
+	__box(_Tp&& __t)
+	noexcept(is_nothrow_move_constructible_v<_Tp>)
+	: _M_value{std::move(__t)}
+	{ }
+
+	template<typename... _Args>
+	  requires constructible_from<_Tp, _Args...>
+	  constexpr
+	  __box(in_place_t, _Args&&... __args)
+	  noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
+	  : _M_value{std::forward<_Args>(__args)...}
+	  { }
+
+	constexpr bool
+	has_value() const noexcept
+	{ return true; };
+
+	constexpr _Tp&
+	operator*() noexcept
+	{ return _M_value; }
+
+	constexpr const _Tp&
+	operator*() const noexcept
+	{ return _M_value; }
+
+	constexpr _Tp*
+	operator->() noexcept
+	{ return &_M_value; }
+
+	constexpr const _Tp*
+	operator->() const noexcept
+	{ return &_M_value; }
+      };
   } // namespace __detail
 
   /// A view that contains exactly one element.
@@ -185,7 +241,7 @@ namespace ranges
       { return _M_value.operator->(); }
 
     private:
-      __detail::__box<_Tp> _M_value;
+      [[no_unique_address]] __detail::__box<_Tp> _M_value;
     };
 
   namespace __detail
@@ -1195,7 +1251,7 @@ namespace views
       };
 
       _Vp _M_base = _Vp();
-      __detail::__box<_Pred> _M_pred;
+      [[no_unique_address]] __detail::__box<_Pred> _M_pred;
       [[no_unique_address]] __detail::_CachedPosition<_Vp> _M_cached_begin;
 
     public:
@@ -1533,7 +1589,7 @@ namespace views
 	};
 
       _Vp _M_base = _Vp();
-      __detail::__box<_Fp> _M_fun;
+      [[no_unique_address]] __detail::__box<_Fp> _M_fun;
 
     public:
       transform_view() = default;
@@ -1787,7 +1843,7 @@ namespace views
 	};
 
       _Vp _M_base = _Vp();
-      __detail::__box<_Pred> _M_pred;
+      [[no_unique_address]] __detail::__box<_Pred> _M_pred;
 
     public:
       take_while_view() = default;
@@ -1947,7 +2003,7 @@ namespace views
     {
     private:
       _Vp _M_base = _Vp();
-      __detail::__box<_Pred> _M_pred;
+      [[no_unique_address]] __detail::__box<_Pred> _M_pred;
       [[no_unique_address]] __detail::_CachedPosition<_Vp> _M_cached_begin;
 
     public:
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc
new file mode 100644
index 00000000000..392acff3eb6
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc
@@ -0,0 +1,73 @@
+// Copyright (C) 2020 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++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <ranges>
+
+using std::ranges::__detail::__box;
+
+using T = decltype([] { return 0; });
+static_assert(std::is_empty_v<__box<T>>);
+static_assert(std::is_nothrow_copy_constructible_v<__box<T>>);
+static_assert(std::is_nothrow_move_constructible_v<__box<T>>);
+static_assert(std::is_nothrow_constructible_v<__box<T>, std::in_place_t>);
+static_assert(requires (__box<T> a) {
+  a = a;
+  a = std::move(a);
+  a.operator*();
+  a.operator->();
+  a.has_value();
+});
+
+struct S
+{
+  S();
+  ~S();
+  S(const S&);
+  S(S&&);
+  S& operator=(const S&);
+  S& operator=(S&&);
+};
+static_assert(std::is_empty_v<__box<S>>);
+static_assert(!std::is_nothrow_copy_constructible_v<__box<S>>
+	      && std::is_copy_constructible_v<__box<S>>);
+static_assert(!std::is_nothrow_move_constructible_v<__box<S>>
+	      && std::is_move_constructible_v<__box<S>>);
+static_assert(!std::is_nothrow_constructible_v<__box<S>, std::in_place_t>
+	      && std::is_constructible_v<__box<S>, std::in_place_t>);
+static_assert(requires (__box<S> a) {
+  a = a;
+  a = std::move(a);
+  a.operator*();
+  a.operator->();
+  a.has_value();
+});
+
+using U = decltype([i=0] { return 0; });
+static_assert(!std::is_empty_v<__box<U>>);
+static_assert(std::is_nothrow_copy_constructible_v<__box<U>>);
+static_assert(std::is_nothrow_move_constructible_v<__box<U>>);
+static_assert(!std::is_nothrow_constructible_v<__box<U>, std::in_place_t>);
+static_assert(requires (__box<U> a) {
+  a = a;
+  a = std::move(a);
+  a.operator*();
+  a.operator->();
+  a.has_value();
+});


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-09-24 16:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-24 16:59 [gcc r11-3441] libstdc++: Specialize ranges::__detail::__box for semiregular types Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).