Skip to content

Commit a4f0e3f

Browse files
committed
Break __memory_resource_adaptor into its own header
This diff pulls `__memory_resource_adaptor` out of `function.hpp` and into its own header.
1 parent 523665a commit a4f0e3f

2 files changed

Lines changed: 125 additions & 87 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* Copyright (c) 2026 Ian Petersen
2+
* Copyright (c) 2026 NVIDIA Corporation
3+
*
4+
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
5+
* (the "License"); you may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* https://llvm.org/LICENSE.txt
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#pragma once
17+
18+
#include "../stdexec/__detail/__concepts.hpp"
19+
20+
#include <cstddef>
21+
#include <memory>
22+
#include <memory_resource>
23+
24+
#include "../stdexec/__detail/__prologue.hpp"
25+
26+
namespace experimental::execution
27+
{
28+
namespace __mem_rsc_adpt
29+
{
30+
using namespace STDEXEC;
31+
32+
template <class _Adaptee>
33+
struct __memory_resource_adaptor;
34+
35+
//! Handle the case that _Adaptee is an allocator of std::bytes
36+
//!
37+
//! Implement do_allocate and do_deallocate in terms of _Adaptee's allocate and
38+
//! deallocate, respectively. Implement do_is_equal in terms of _Adaptee's
39+
//! operator==.
40+
template <class _Adaptee>
41+
requires __simple_allocator<_Adaptee>
42+
&& __same_as<std::byte, typename std::allocator_traits<_Adaptee>::value_type>
43+
struct __memory_resource_adaptor<_Adaptee>
44+
{
45+
//! Implement memory_resource in terms of an allocator<std::byte>
46+
class type : public std::pmr::memory_resource
47+
{
48+
using __traits = std::allocator_traits<_Adaptee>;
49+
static_assert(__same_as<std::byte, typename __traits::value_type>);
50+
typename __traits::allocator_type __alloc_;
51+
52+
public:
53+
template <class _Alloc>
54+
requires(!__same_as<_Alloc, type>)
55+
constexpr explicit type(_Alloc const &__alloc) noexcept
56+
: __alloc_(__alloc)
57+
{
58+
using __rebound_traits = std::allocator_traits<_Alloc>::template rebind_traits<std::byte>;
59+
static_assert(__same_as<__traits, __rebound_traits>);
60+
}
61+
62+
constexpr void *do_allocate(std::size_t __bytes, std::size_t __align) override
63+
{
64+
// TODO: we're not using __align, which is probably a bug
65+
return __traits::allocate(__alloc_, __bytes);
66+
}
67+
68+
constexpr void do_deallocate(void *__p, std::size_t __bytes, std::size_t __align) override
69+
{
70+
// TODO: we're not using __align, which is probably a bug
71+
__traits::deallocate(__alloc_, new (__p) std::byte[__bytes], __bytes);
72+
}
73+
74+
constexpr bool do_is_equal(std::pmr::memory_resource const &__other) const noexcept override
75+
{
76+
if (auto *__ptr = dynamic_cast<type const *>(&__other))
77+
{
78+
return __alloc_ == __ptr->__alloc_;
79+
}
80+
81+
return false;
82+
}
83+
};
84+
};
85+
86+
//! Handle the case that _Adaptee is an allocator of some type other than std::byte
87+
//!
88+
//! We just rebind _Adaptee to be an allocator of std::bytes and inherit our nested
89+
//! alias from the adaptor for that type. This strategy ensures that there's only one
90+
//! adaptor for an entire family of adapted allocator types, reducing template bloat
91+
//! and making the do_is_equals implementation sensible.
92+
template <class _Adaptee>
93+
requires __simple_allocator<_Adaptee>
94+
struct __memory_resource_adaptor<_Adaptee>
95+
: __memory_resource_adaptor<
96+
typename std::allocator_traits<_Adaptee>::template rebind_alloc<std::byte>>
97+
{};
98+
99+
//! Handle the case that _Adaptee is a pointer to a type that derives from
100+
//! std::pmr::memory_resource
101+
//!
102+
//! In this case, there's nothing to "adapt" so we just alias _Adaptee.
103+
template <class _Adaptee>
104+
requires __std::constructible_from<std::pmr::polymorphic_allocator<std::byte>, _Adaptee>
105+
struct __memory_resource_adaptor<_Adaptee>
106+
{
107+
using type = _Adaptee;
108+
};
109+
} // namespace __mem_rsc_adpt
110+
111+
//! Adapt _Adaptee to be a std::pmr::memory_resource
112+
//!
113+
//! This alias is the identity when _Adaptee is a pointer to a type that derives from
114+
//! std::pmr::memory_resource. When _Adaptee is an allocator type, it is a type that
115+
//! derives from std::pmr::memory_resource and implements its pure-virtual member
116+
//! functions in terms of that allocator type rebound to std::byte.
117+
template <class _Adaptee>
118+
using __memory_resource_adaptor_t =
119+
__mem_rsc_adpt::__memory_resource_adaptor<std::remove_cvref_t<_Adaptee>>::type;
120+
} // namespace experimental::execution
121+
122+
namespace exec = experimental::execution;
123+
124+
#include "../stdexec/__detail/__epilogue.hpp"

include/exec/function.hpp

Lines changed: 1 addition & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
// TODO: split this header into pieces
3131
#include "__frame_allocator.hpp"
32+
#include "__memory_resource_adaptor.hpp"
3233
#include "any_sender_of.hpp"
3334
#include "get_frame_allocator.hpp"
3435

@@ -114,93 +115,6 @@ namespace experimental::execution
114115
__prop_t *__env_;
115116
};
116117

117-
//! Adapt _Adaptee to be a std::pmr::memory_resource
118-
//!
119-
//! The alias __memory_resource_adaptor_t<_Adaptee> is the identity when _Adaptee is
120-
//! a pointer to a type that derives from std::pmr::memory_resource. When _Adaptee
121-
//! is an allocator type, __memory_resource_adaptor_t<_Adaptee> is a type that
122-
//! derives from std::pmr::memory_resource and implements its pure-virtual member
123-
//! functions in terms of that allocator type rebound to std::byte.
124-
template <class _Adaptee>
125-
struct __memory_resource_adaptor;
126-
127-
//! Handle the case that _Adaptee is an allocator of std::bytes
128-
//!
129-
//! Implement do_allocate and do_deallocate in terms of _Adaptee's allocate and
130-
//! deallocate, respectively. Implement do_is_equal in terms of _Adaptee's
131-
//! operator==.
132-
template <class _Adaptee>
133-
requires __simple_allocator<_Adaptee>
134-
&& __same_as<std::byte, typename std::allocator_traits<_Adaptee>::value_type>
135-
struct __memory_resource_adaptor<_Adaptee>
136-
{
137-
//! Implement memory_resource in terms of an allocator<std::byte>
138-
class type : public std::pmr::memory_resource
139-
{
140-
using __traits = std::allocator_traits<_Adaptee>;
141-
static_assert(__same_as<std::byte, typename __traits::value_type>);
142-
typename __traits::allocator_type __alloc_;
143-
144-
public:
145-
template <class _Alloc>
146-
requires(!__same_as<_Alloc, type>)
147-
constexpr explicit type(_Alloc const &__alloc) noexcept
148-
: __alloc_(__alloc)
149-
{
150-
using __rebound_traits = std::allocator_traits<_Alloc>::template rebind_traits<std::byte>;
151-
static_assert(__same_as<__traits, __rebound_traits>);
152-
}
153-
154-
constexpr void *do_allocate(std::size_t __bytes, std::size_t __align) override
155-
{
156-
return __traits::allocate(__alloc_, __bytes);
157-
}
158-
159-
constexpr void do_deallocate(void *__p, std::size_t __bytes, std::size_t __align) override
160-
{
161-
__traits::deallocate(__alloc_, new (__p) std::byte[__bytes], __bytes);
162-
}
163-
164-
constexpr bool do_is_equal(std::pmr::memory_resource const &__other) const noexcept override
165-
{
166-
if (auto *__ptr = dynamic_cast<type const *>(&__other))
167-
{
168-
return __alloc_ == __ptr->__alloc_;
169-
}
170-
171-
return false;
172-
}
173-
};
174-
};
175-
176-
//! Handle the case that _Adaptee is an allocator of some type other than std::byte
177-
//!
178-
//! We just rebind _Adaptee to be an allocator of std::bytes and inherit our nested
179-
//! alias from the adaptor for that type. This strategy ensures that there's only one
180-
//! adaptor for an entire family of adapted allocator types, reducing template bloat
181-
//! and making the do_is_equals implementation sensible.
182-
template <class _Adaptee>
183-
requires __simple_allocator<_Adaptee>
184-
struct __memory_resource_adaptor<_Adaptee>
185-
: __memory_resource_adaptor<
186-
typename std::allocator_traits<_Adaptee>::template rebind_alloc<std::byte>>
187-
{};
188-
189-
//! Handle the case that _Adaptee is a pointer to a type that derives from
190-
//! std::pmr::memory_resource
191-
//!
192-
//! In this case, there's nothing to "adapt" so we just alias _Adaptee.
193-
template <class _Adaptee>
194-
requires __std::constructible_from<std::pmr::polymorphic_allocator<std::byte>, _Adaptee>
195-
struct __memory_resource_adaptor<_Adaptee>
196-
{
197-
using type = _Adaptee;
198-
};
199-
200-
template <class _Adaptee>
201-
using __memory_resource_adaptor_t =
202-
__memory_resource_adaptor<std::remove_cvref_t<_Adaptee>>::type;
203-
204118
template <class _Sigs, class _Queries>
205119
using __any_receiver_ref = ::exec::_any::_any_receiver_ref<_Sigs, _Queries>;
206120

0 commit comments

Comments
 (0)