[C++,coroutines,5/6] Standard library header.
diff mbox series

Message ID 3D9E9C45-F7A2-42E4-B0B0-51B03A1041F0@sandoe.co.uk
State New
Headers show
Series
  • Implement C++ coroutines.
Related show

Commit Message

Iain Sandoe Nov. 17, 2019, 10:27 a.m. UTC
This provides the interfaces mandated by the standard and implements
the interaction with the coroutine frame by means of inline use of
builtins expanded at compile-time.  There should be a 1:1 correspondence
with the standard sections which are cross-referenced.

There is no runtime content.

At this stage we have the content in an inline namespace "n4835" for
the current CD.

libstdc++-v3/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* include/Makefile.am: Add coroutine to the experimental set.
	* include/Makefile.in: Regnerated.
	* include/experimental/coroutine: New file.
---
 libstdc++-v3/include/Makefile.am            |   1 +
 libstdc++-v3/include/Makefile.in            |   1 +
 libstdc++-v3/include/experimental/coroutine | 268 ++++++++++++++++++++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 libstdc++-v3/include/experimental/coroutine

Comments

Jonathan Wakely Nov. 17, 2019, 4:19 p.m. UTC | #1
On 17/11/19 10:27 +0000, Iain Sandoe wrote:
>This provides the interfaces mandated by the standard and implements
>the interaction with the coroutine frame by means of inline use of
>builtins expanded at compile-time.  There should be a 1:1 correspondence
>with the standard sections which are cross-referenced.
>
>There is no runtime content.
>
>At this stage we have the content in an inline namespace "n4835" for
>the current CD.
>
>libstdc++-v3/ChangeLog:
>
>2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>
>	* include/Makefile.am: Add coroutine to the experimental set.
>	* include/Makefile.in: Regnerated.

"Regnerated" typo.

>	* include/experimental/coroutine: New file.
>---
> libstdc++-v3/include/Makefile.am            |   1 +
> libstdc++-v3/include/Makefile.in            |   1 +
> libstdc++-v3/include/experimental/coroutine | 268 ++++++++++++++++++++++++++++
> 3 files changed, 270 insertions(+)
> create mode 100644 libstdc++-v3/include/experimental/coroutine
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 49fd413..4ffe209 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -708,6 +708,7 @@ experimental_headers = \
> 	${experimental_srcdir}/array \
> 	${experimental_srcdir}/buffer \
> 	${experimental_srcdir}/chrono \
>+	${experimental_srcdir}/coroutine \

The experimental dir is (currently) only used for TS headers. All
C++20 support is currently experimental, so adding <coroutines> where
<concepts> and <ranges> have been added would be OK.

But I'm not really clear if this is an implementation of the TS or the
C++20 feature.  If it's a hybrid, putting it in
<experimental/coroutines> is fine.

When the final <coroutines> header is added it will need to be in
libsupc++ so that it's included for freestanding builds (and at that
point it won't be able to use <bits/stl_function.h>, but that will be
OK as the final header will be C++20-only and can rely on <compare>
unconditionally, which is also freestanding).

> 	${experimental_srcdir}/deque \
> 	${experimental_srcdir}/executor \
> 	${experimental_srcdir}/forward_list \


>diff --git a/libstdc++-v3/include/experimental/coroutine b/libstdc++-v3/include/experimental/coroutine
>new file mode 100644
>index 0000000..d903352
>--- /dev/null
>+++ b/libstdc++-v3/include/experimental/coroutine
>@@ -0,0 +1,268 @@
>+// <experimental/coroutine> -*- C++ -*-
>+
>+// Copyright (C) 2019 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.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file experimental/coroutine
>+ *  This is an experimental C++ Library header against the C++20 CD n4835.
>+ *  @ingroup coroutine-ts

The coroutine-ts doc group should be defined somewhere.

>+ */
>+
>+#ifndef _GLIBCXX_EXPERIMENTAL_COROUTINE
>+#define _GLIBCXX_EXPERIMENTAL_COROUTINE 1
>+
>+#pragma GCC system_header
>+
>+// It is very likely that earlier versions would work, but they are untested.
>+#if __cplusplus >= 201402L
>+
>+#include <bits/c++config.h>
>+
>+#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
>+#  include <compare>
>+#  define THE_SPACESHIP_HAS_LANDED 1

This is in trunk now, although not supported by Clang, and not
supported by GCC pre-C++20, so the fallback is OK.

The macro name should be a reserved name though, e.g.
_THE_SPACESHIP_HAS_LANDED

>+#else
>+#  include <bits/stl_function.h>
>+#  define THE_SPACESHIP_HAS_LANDED 0
>+#endif
>+
>+namespace std _GLIBCXX_VISIBILITY (default)
>+{
>+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+#if __cpp_coroutines
>+
>+  namespace experimental {
>+  inline namespace coroutines_n4835 {

This should be a reserved name too, e.g. __coroutines_n4835.

>+
>+  // [coroutine.traits]
>+  // [coroutine.traits.primary]
>+  // 17.12.2 coroutine traits
>+  template <typename _R, typename...> struct coroutine_traits
>+  {
>+    using promise_type = typename _R::promise_type;
>+  };
>+
>+  // 17.12.3 Class template coroutine_handle
>+  // [coroutine.handle]
>+  template <typename _Promise = void> struct coroutine_handle;
>+
>+  template <> struct coroutine_handle<void>
>+  {
>+  public:
>+    // 17.12.3.1, construct/reset
>+    constexpr coroutine_handle () noexcept : __fr_ptr (0) {}

The libstdc++ naming convention is _M_xxx for non-static members (both
data members and member functions) and _S_xxx for static members
(again, both data member and functions).

This helps to distinguish members from other uglified names like
function parameters and local variables.

Patch
diff mbox series

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 49fd413..4ffe209 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -708,6 +708,7 @@  experimental_headers = \
 	${experimental_srcdir}/array \
 	${experimental_srcdir}/buffer \
 	${experimental_srcdir}/chrono \
+	${experimental_srcdir}/coroutine \
 	${experimental_srcdir}/deque \
 	${experimental_srcdir}/executor \
 	${experimental_srcdir}/forward_list \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index acc4fe5..fdb7d3d 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -1052,6 +1052,7 @@  experimental_headers = \
 	${experimental_srcdir}/array \
 	${experimental_srcdir}/buffer \
 	${experimental_srcdir}/chrono \
+	${experimental_srcdir}/coroutine \
 	${experimental_srcdir}/deque \
 	${experimental_srcdir}/executor \
 	${experimental_srcdir}/forward_list \
diff --git a/libstdc++-v3/include/experimental/coroutine b/libstdc++-v3/include/experimental/coroutine
new file mode 100644
index 0000000..d903352
--- /dev/null
+++ b/libstdc++-v3/include/experimental/coroutine
@@ -0,0 +1,268 @@ 
+// <experimental/coroutine> -*- C++ -*-
+
+// Copyright (C) 2019 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file experimental/coroutine
+ *  This is an experimental C++ Library header against the C++20 CD n4835.
+ *  @ingroup coroutine-ts
+ */
+
+#ifndef _GLIBCXX_EXPERIMENTAL_COROUTINE
+#define _GLIBCXX_EXPERIMENTAL_COROUTINE 1
+
+#pragma GCC system_header
+
+// It is very likely that earlier versions would work, but they are untested.
+#if __cplusplus >= 201402L
+
+#include <bits/c++config.h>
+
+#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
+#  include <compare>
+#  define THE_SPACESHIP_HAS_LANDED 1
+#else
+#  include <bits/stl_function.h>
+#  define THE_SPACESHIP_HAS_LANDED 0
+#endif
+
+namespace std _GLIBCXX_VISIBILITY (default)
+{
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#if __cpp_coroutines
+
+  namespace experimental {
+  inline namespace coroutines_n4835 {
+
+  // [coroutine.traits]
+  // [coroutine.traits.primary]
+  // 17.12.2 coroutine traits
+  template <typename _R, typename...> struct coroutine_traits
+  {
+    using promise_type = typename _R::promise_type;
+  };
+
+  // 17.12.3 Class template coroutine_handle
+  // [coroutine.handle]
+  template <typename _Promise = void> struct coroutine_handle;
+
+  template <> struct coroutine_handle<void>
+  {
+  public:
+    // 17.12.3.1, construct/reset
+    constexpr coroutine_handle () noexcept : __fr_ptr (0) {}
+    constexpr coroutine_handle (decltype (nullptr) __h) noexcept
+      : __fr_ptr (__h)
+    {}
+    coroutine_handle &operator= (decltype (nullptr)) noexcept
+    {
+      __fr_ptr = nullptr;
+      return *this;
+    }
+
+  public:
+    // 17.12.3.2, export/import
+    constexpr void *address () const noexcept { return __fr_ptr; }
+    constexpr static coroutine_handle from_address (void *__a) noexcept
+    {
+      coroutine_handle __self;
+      __self.__fr_ptr = __a;
+      return __self;
+    }
+
+  public:
+    // 17.12.3.3, observers
+    constexpr explicit operator bool () const noexcept
+    {
+      return bool(__fr_ptr);
+    }
+    bool done () const noexcept { return __builtin_coro_done (__fr_ptr); }
+    // 17.12.3.4, resumption
+    void operator() () const { resume (); }
+    void resume () const { __builtin_coro_resume (__fr_ptr); }
+    void destroy () const { __builtin_coro_destroy (__fr_ptr); }
+
+  protected:
+    void *__fr_ptr;
+  };
+
+  // [coroutine.handle.compare]
+  // 17.12.3.6 Comparison operators
+  constexpr bool operator== (coroutine_handle<> __a,
+			     coroutine_handle<> __b) noexcept
+  {
+    return __a.address () == __b.address ();
+  }
+
+#if THE_SPACESHIP_HAS_LANDED
+  constexpr strong_ordering
+  operator<=> (coroutine_handle<> __a, coroutine_handle<> __b) noexcept;
+#else
+  // These are from the TS.
+  constexpr bool operator!= (coroutine_handle<> __a,
+			     coroutine_handle<> __b) noexcept
+  {
+    return !(__a == __b);
+  }
+
+  constexpr bool operator< (coroutine_handle<> __a,
+			    coroutine_handle<> __b) noexcept
+  {
+    return less<void *> () (__a.address (), __b.address ());
+  }
+
+  constexpr bool operator> (coroutine_handle<> __a,
+			    coroutine_handle<> __b) noexcept
+  {
+    return __b < __a;
+  }
+
+  constexpr bool operator<= (coroutine_handle<> __a,
+			     coroutine_handle<> __b) noexcept
+  {
+    return !(__a > __b);
+  }
+
+  constexpr bool operator>= (coroutine_handle<> __a,
+			     coroutine_handle<> __b) noexcept
+  {
+    return !(__a < __b);
+  }
+#endif
+
+  template <class _Promise> struct coroutine_handle : coroutine_handle<>
+  {
+    // 17.12.3.1, construct/reset
+    using coroutine_handle<>::coroutine_handle;
+    static coroutine_handle from_promise (_Promise &p)
+    {
+      coroutine_handle __self;
+      __self.__fr_ptr
+	= __builtin_coro_promise ((char *) &p, __alignof(_Promise), true);
+      return __self;
+    }
+    coroutine_handle &operator= (decltype (nullptr)) noexcept
+    {
+      coroutine_handle<>::operator= (nullptr);
+      return *this;
+    }
+    // 17.12.3.2, export/import
+    constexpr static coroutine_handle from_address (void *__a)
+    {
+      coroutine_handle __self;
+      __self.__fr_ptr = __a;
+      return __self;
+    }
+    // 17.12.3.5, promise accesss
+    _Promise &promise () const
+    {
+      void *__t
+	= __builtin_coro_promise (this->__fr_ptr, __alignof(_Promise), false);
+      return *static_cast<_Promise *> (__t);
+    }
+  };
+
+  // [coroutine.noop]
+  struct noop_coroutine_promise
+  {
+  };
+
+  void __dummy_resume_destroy () __attribute__ ((__weak__));
+  void __dummy_resume_destroy () {}
+
+  struct __noop_coro_frame
+  {
+    void (*__r) () = __dummy_resume_destroy;
+    void (*__d) () = __dummy_resume_destroy;
+    struct noop_coroutine_promise __p;
+  } __noop_coro_fr __attribute__ ((__weak__));
+
+  // [coroutine.promise.noop]
+  // 17.12.4.1 Class noop_coroutine_promise
+  template <>
+  class coroutine_handle<noop_coroutine_promise> : public coroutine_handle<>
+  {
+    using _Promise = noop_coroutine_promise;
+
+  public:
+    // // 17.12.4.2.1, observers
+    constexpr explicit operator bool () const noexcept { return true; }
+    constexpr bool done () const noexcept { return false; }
+
+    // 17.12.4.2.2, resumption
+    void operator() () const noexcept {}
+    void resume () const noexcept {}
+    void destroy () const noexcept {}
+
+    // 17.12.4.2.3, promise access
+    _Promise &promise () const
+    {
+      return *static_cast<_Promise *> (
+	__builtin_coro_promise (this->__fr_ptr, __alignof(_Promise), false));
+    }
+
+    // 17.12.4.2.4, address
+    // constexpr void* address() const noexcept;
+  private:
+    friend coroutine_handle<noop_coroutine_promise> noop_coroutine () noexcept;
+
+    coroutine_handle () noexcept { this->__fr_ptr = (void *) &__noop_coro_fr; }
+  };
+
+  using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+
+  inline noop_coroutine_handle noop_coroutine () noexcept
+  {
+    return noop_coroutine_handle ();
+  }
+
+  // [coroutine.trivial.awaitables]
+  // 17.12.5 Trivial awaitables
+
+  struct suspend_always
+  {
+    bool await_ready () { return false; }
+    void await_suspend (coroutine_handle<>) {}
+    void await_resume () {}
+  };
+
+  struct suspend_never
+  {
+    bool await_ready () { return true; }
+    void await_suspend (coroutine_handle<>) {}
+    void await_resume () {}
+  };
+
+  } // namespace coroutines_n4835
+  } // namespace experimental
+
+#else
+#error "the coroutine header requires -fcoroutines"
+#endif
+
+  _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std_GLIBCXX_VISIBILITY(default)
+
+#endif // C++14
+
+#endif // _GLIBCXX_EXPERIMENTAL_COROUTINE