1*f083bc1aSPatrick Williams /*
2*f083bc1aSPatrick Williams  * Copyright (c) 2023 Maikel Nadolski
3*f083bc1aSPatrick Williams  * Copyright (c) 2023 NVIDIA Corporation
4*f083bc1aSPatrick Williams  *
5*f083bc1aSPatrick Williams  * Licensed under the Apache License Version 2.0 with LLVM Exceptions
6*f083bc1aSPatrick Williams  * (the "License"); you may not use this file except in compliance with
7*f083bc1aSPatrick Williams  * the License. You may obtain a copy of the License at
8*f083bc1aSPatrick Williams  *
9*f083bc1aSPatrick Williams  *   https://llvm.org/LICENSE.txt
10*f083bc1aSPatrick Williams  *
11*f083bc1aSPatrick Williams  * Unless required by applicable law or agreed to in writing, software
12*f083bc1aSPatrick Williams  * distributed under the License is distributed on an "AS IS" BASIS,
13*f083bc1aSPatrick Williams  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*f083bc1aSPatrick Williams  * See the License for the specific language governing permissions and
15*f083bc1aSPatrick Williams  * limitations under the License.
16*f083bc1aSPatrick Williams  */
17*f083bc1aSPatrick Williams #pragma once
18*f083bc1aSPatrick Williams 
19*f083bc1aSPatrick Williams #include "../concepts.hpp"
20*f083bc1aSPatrick Williams 
21*f083bc1aSPatrick Williams #include <cstddef>
22*f083bc1aSPatrick Williams #include <memory>
23*f083bc1aSPatrick Williams #include <new>
24*f083bc1aSPatrick Williams #include <type_traits>
25*f083bc1aSPatrick Williams 
26*f083bc1aSPatrick Williams namespace stdexec
27*f083bc1aSPatrick Williams {
28*f083bc1aSPatrick Williams 
29*f083bc1aSPatrick Williams //! Holds storage for a `_Ty`, but allows clients to `__construct(...)`,
30*f083bc1aSPatrick Williams //! `__destry()`, and `__get()` the `_Ty` without regard for usual lifetime
31*f083bc1aSPatrick Williams //! rules.
32*f083bc1aSPatrick Williams template <class _Ty>
33*f083bc1aSPatrick Williams class __manual_lifetime
34*f083bc1aSPatrick Williams {
35*f083bc1aSPatrick Williams   public:
36*f083bc1aSPatrick Williams     //! Constructor does nothing: It's on you to call `__construct(...)` or
37*f083bc1aSPatrick Williams     //! `__construct_from(...)` if you want the `_Ty`'s lifetime to begin.
__manual_lifetime()38*f083bc1aSPatrick Williams     constexpr __manual_lifetime() noexcept {}
39*f083bc1aSPatrick Williams     //! Destructor does nothing: It's on you to call `__destroy()` if you mean
40*f083bc1aSPatrick Williams     //! to.
~__manual_lifetime()41*f083bc1aSPatrick Williams     constexpr ~__manual_lifetime() {}
42*f083bc1aSPatrick Williams 
43*f083bc1aSPatrick Williams     __manual_lifetime(const __manual_lifetime&) = delete;
44*f083bc1aSPatrick Williams     auto operator=(const __manual_lifetime&) -> __manual_lifetime& = delete;
45*f083bc1aSPatrick Williams 
46*f083bc1aSPatrick Williams     __manual_lifetime(__manual_lifetime&&) = delete;
47*f083bc1aSPatrick Williams     auto operator=(__manual_lifetime&&) -> __manual_lifetime& = delete;
48*f083bc1aSPatrick Williams 
49*f083bc1aSPatrick Williams     //! Construct the `_Ty` in place.
50*f083bc1aSPatrick Williams     //! There are no safeties guarding against the case that there's already one
51*f083bc1aSPatrick Williams     //! there.
52*f083bc1aSPatrick Williams     template <class... _Args>
__construct(_Args &&...__args)53*f083bc1aSPatrick Williams     auto __construct(_Args&&... __args) noexcept(
54*f083bc1aSPatrick Williams         stdexec::__nothrow_constructible_from<_Ty, _Args...>) -> _Ty&
55*f083bc1aSPatrick Williams     {
56*f083bc1aSPatrick Williams         // Use placement new instead of std::construct_at to support aggregate
57*f083bc1aSPatrick Williams         // initialization with brace elision.
58*f083bc1aSPatrick Williams         return *std::launder(::new (static_cast<void*>(__buffer_))
59*f083bc1aSPatrick Williams                                  _Ty{static_cast<_Args&&>(__args)...});
60*f083bc1aSPatrick Williams     }
61*f083bc1aSPatrick Williams 
62*f083bc1aSPatrick Williams     //! Construct the `_Ty` in place from the result of calling `func`.
63*f083bc1aSPatrick Williams     //! There are no safeties guarding against the case that there's already one
64*f083bc1aSPatrick Williams     //! there.
65*f083bc1aSPatrick Williams     template <class _Func, class... _Args>
__construct_from(_Func && func,_Args &&...__args)66*f083bc1aSPatrick Williams     auto __construct_from(_Func&& func, _Args&&... __args) -> _Ty&
67*f083bc1aSPatrick Williams     {
68*f083bc1aSPatrick Williams         // Use placement new instead of std::construct_at in case the function
69*f083bc1aSPatrick Williams         // returns an immovable type.
70*f083bc1aSPatrick Williams         return *std::launder(::new (static_cast<void*>(__buffer_)) _Ty{
71*f083bc1aSPatrick Williams             (static_cast<_Func&&>(func))(static_cast<_Args&&>(__args)...)});
72*f083bc1aSPatrick Williams     }
73*f083bc1aSPatrick Williams     //! End the lifetime of the contained `_Ty`.
74*f083bc1aSPatrick Williams     //! Precondition: The lifetime has started.
__destroy()75*f083bc1aSPatrick Williams     void __destroy() noexcept
76*f083bc1aSPatrick Williams     {
77*f083bc1aSPatrick Williams         std::destroy_at(&__get());
78*f083bc1aSPatrick Williams     }
79*f083bc1aSPatrick Williams     //! Get access to the `_Ty`.
80*f083bc1aSPatrick Williams     //! Precondition: The lifetime has started.
__get()81*f083bc1aSPatrick Williams     auto __get() & noexcept -> _Ty&
82*f083bc1aSPatrick Williams     {
83*f083bc1aSPatrick Williams         return *reinterpret_cast<_Ty*>(__buffer_);
84*f083bc1aSPatrick Williams     }
85*f083bc1aSPatrick Williams 
86*f083bc1aSPatrick Williams     //! Get access to the `_Ty`.
87*f083bc1aSPatrick Williams     //! Precondition: The lifetime has started.
__get()88*f083bc1aSPatrick Williams     auto __get() && noexcept -> _Ty&&
89*f083bc1aSPatrick Williams     {
90*f083bc1aSPatrick Williams         return static_cast<_Ty&&>(*reinterpret_cast<_Ty*>(__buffer_));
91*f083bc1aSPatrick Williams     }
92*f083bc1aSPatrick Williams 
93*f083bc1aSPatrick Williams     //! Get access to the `_Ty`.
94*f083bc1aSPatrick Williams     //! Precondition: The lifetime has started.
__get() const95*f083bc1aSPatrick Williams     auto __get() const& noexcept -> const _Ty&
96*f083bc1aSPatrick Williams     {
97*f083bc1aSPatrick Williams         return *reinterpret_cast<const _Ty*>(__buffer_);
98*f083bc1aSPatrick Williams     }
99*f083bc1aSPatrick Williams 
100*f083bc1aSPatrick Williams     //! Move semantics aren't supported.
101*f083bc1aSPatrick Williams     //! If you want to move the `_Ty`, use `std::move(ml.__get())`.
102*f083bc1aSPatrick Williams     auto __get() const&& noexcept -> const _Ty&& = delete;
103*f083bc1aSPatrick Williams 
104*f083bc1aSPatrick Williams   private:
105*f083bc1aSPatrick Williams     alignas(_Ty) unsigned char __buffer_[sizeof(_Ty)]{};
106*f083bc1aSPatrick Williams };
107*f083bc1aSPatrick Williams } // namespace stdexec
108