On Thu, Apr 24, 2025 at 2:41 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> This was approved in Wrocław as LWG 3899.
>
> This avoids creating a new coroutine frame to co_yield the elements of
> an lvalue generator.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/generator (generator::yield_value): Add overload
>         taking lvalue element_of view, as per LWG 3899.
>         * testsuite/24_iterators/range_generators/lwg3899.cc: New test.
> ---
>
> Added a testcase (thanks to Arsen).
>
> Tested x86_64-linux.
>
LGTM

>
>  libstdc++-v3/include/std/generator            | 10 ++++
>  .../24_iterators/range_generators/lwg3899.cc  | 57 +++++++++++++++++++
>  2 files changed, 67 insertions(+)
>  create mode 100644
> libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc
>
> diff --git a/libstdc++-v3/include/std/generator
> b/libstdc++-v3/include/std/generator
> index 3f781f1bb29..7ab2c9e7ce9 100644
> --- a/libstdc++-v3/include/std/generator
> +++ b/libstdc++-v3/include/std/generator
> @@ -153,6 +153,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           noexcept
>         { return _Recursive_awaiter { std::move(__r.range) }; }
>
> +       // _GLIBCXX_RESOLVE_LIB_DEFECTS
> +       // 3899. co_yielding elements of an lvalue generator is
> +       // unnecessarily inefficient
> +       template<typename _R2, typename _V2, typename _A2, typename _U2>
> +       requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded>
> +       auto
> +       yield_value(ranges::elements_of<generator<_R2, _V2, _A2>&, _U2>
> __r)
> +         noexcept
> +       { return _Recursive_awaiter { std::move(__r.range) }; }
> +
>         template<ranges::input_range _R, typename _Alloc>
>         requires convertible_to<ranges::range_reference_t<_R>, _Yielded>
>         auto
> diff --git
> a/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc
> b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc
> new file mode 100644
> index 00000000000..5a812ecf9d9
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc
> @@ -0,0 +1,57 @@
> +// { dg-do run { target c++23 } }
> +
> +// LWG 3899.
> +// co_yielding elements of an lvalue generator is unnecessarily
> inefficient
> +
> +#include <generator>
> +#include <memory_resource>
> +#include <testsuite_hooks.h>
> +
> +struct memory_resource : std::pmr::memory_resource
> +{
> +  std::size_t count = 0;
> +
> +  void* do_allocate(std::size_t n, std::size_t a) override
> +  {
> +    count += n;
> +    return std::pmr::new_delete_resource()->allocate(n, a);
> +  }
> +
> +  void do_deallocate(void* p, std::size_t n, std::size_t a) override
> +  {
> +    return std::pmr::new_delete_resource()->deallocate(p, n, a);
> +  }
> +
> +  bool do_is_equal(const std::pmr::memory_resource& mr) const noexcept
> override
> +  { return this == &mr; }
> +};
> +
> +std::pmr::generator<int>
> +f(std::allocator_arg_t, std::pmr::polymorphic_allocator<>, int init)
> +{
> +  co_yield init + 0;
> +  co_yield init + 1;
> +}
> +
> +std::pmr::generator<int>
> +g(std::allocator_arg_t, std::pmr::polymorphic_allocator<> alloc)
> +{
> +  auto gen = f(std::allocator_arg, alloc, 0);
> +  auto gen2 = f(std::allocator_arg, alloc, 2);
> +  co_yield std::ranges::elements_of(std::move(gen), alloc);
> +  co_yield std::ranges::elements_of(gen2, alloc);
> +}
> +
> +int
> +main()
> +{
> +  std::size_t counts[4];
> +  memory_resource mr;
> +  for (auto d : g(std::allocator_arg , &mr))
> +    counts[d] = mr.count;
> +  VERIFY(counts[0] != 0);
> +  // No allocations after the first one:
> +  VERIFY(counts[1] == counts[0]);
> +  VERIFY(counts[2] == counts[0]);
> +  VERIFY(counts[3] == counts[0]);
> +}
> --
> 2.49.0
>
>

Reply via email to