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