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