Skip to content

Commit a4e8d18

Browse files
committed
libstdc++: Add lvalue overload for generator::yield_value
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. Reviewed-by: Tomasz Kamiński <[email protected]> Reviewed-by: Arsen Arsenović <[email protected]>
1 parent 419f40a commit a4e8d18

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

libstdc++-v3/include/std/generator

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
153153
noexcept
154154
{ return _Recursive_awaiter { std::move(__r.range) }; }
155155

156+
// _GLIBCXX_RESOLVE_LIB_DEFECTS
157+
// 3899. co_yielding elements of an lvalue generator is
158+
// unnecessarily inefficient
159+
template<typename _R2, typename _V2, typename _A2, typename _U2>
160+
requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded>
161+
auto
162+
yield_value(ranges::elements_of<generator<_R2, _V2, _A2>&, _U2> __r)
163+
noexcept
164+
{ return _Recursive_awaiter { std::move(__r.range) }; }
165+
156166
template<ranges::input_range _R, typename _Alloc>
157167
requires convertible_to<ranges::range_reference_t<_R>, _Yielded>
158168
auto
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// { dg-do run { target c++23 } }
2+
3+
// LWG 3899.
4+
// co_yielding elements of an lvalue generator is unnecessarily inefficient
5+
6+
#include <generator>
7+
#include <memory_resource>
8+
#include <testsuite_hooks.h>
9+
10+
struct memory_resource : std::pmr::memory_resource
11+
{
12+
std::size_t count = 0;
13+
14+
void* do_allocate(std::size_t n, std::size_t a) override
15+
{
16+
count += n;
17+
return std::pmr::new_delete_resource()->allocate(n, a);
18+
}
19+
20+
void do_deallocate(void* p, std::size_t n, std::size_t a) override
21+
{
22+
return std::pmr::new_delete_resource()->deallocate(p, n, a);
23+
}
24+
25+
bool do_is_equal(const std::pmr::memory_resource& mr) const noexcept override
26+
{ return this == &mr; }
27+
};
28+
29+
std::pmr::generator<int>
30+
f(std::allocator_arg_t, std::pmr::polymorphic_allocator<>, int init)
31+
{
32+
co_yield init + 0;
33+
co_yield init + 1;
34+
}
35+
36+
std::pmr::generator<int>
37+
g(std::allocator_arg_t, std::pmr::polymorphic_allocator<> alloc)
38+
{
39+
auto gen = f(std::allocator_arg, alloc, 0);
40+
auto gen2 = f(std::allocator_arg, alloc, 2);
41+
co_yield std::ranges::elements_of(std::move(gen), alloc);
42+
co_yield std::ranges::elements_of(gen2, alloc);
43+
}
44+
45+
int
46+
main()
47+
{
48+
std::size_t counts[4];
49+
memory_resource mr;
50+
for (auto d : g(std::allocator_arg , &mr))
51+
counts[d] = mr.count;
52+
VERIFY(counts[0] != 0);
53+
// No allocations after the first one:
54+
VERIFY(counts[1] == counts[0]);
55+
VERIFY(counts[2] == counts[0]);
56+
VERIFY(counts[3] == counts[0]);
57+
}

0 commit comments

Comments
 (0)