Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
fast_pimpl.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cstddef>
5#include <memory>
6#include <new>
7#include <type_traits>
8
9namespace helios::utils {
10
11/**
12 * @brief Implements pimpl idiom without dynamic memory allocation.
13 * @details FastPimpl doesn't require memory allocation or indirect memory
14 * access. You must manually set object size and alignment when instantiating
15 * FastPimpl.
16 * @tparam T The implementation type
17 * @tparam Size The size in bytes to allocate for T
18 * @tparam Alignment The alignment requirement for T
19 * @tparam RequireStrictMatch If true, requires exact size/alignment match
20 */
21template <class T, size_t Size, size_t Alignment,
22 bool RequireStrictMatch = false>
23class FastPimpl {
24public:
25 template <typename... Args>
26 explicit(sizeof...(Args) == 1) FastPimpl(Args&&... args) noexcept(
27 std::is_nothrow_constructible_v<T, Args...>) {
28 std::construct_at(Impl(), std::forward<Args>(args)...);
29 }
30
31 FastPimpl(const FastPimpl& other) noexcept(
32 noexcept(FastPimpl(std::declval<const T&>())))
33 : FastPimpl(*other) {}
34
35 FastPimpl(FastPimpl&& other) noexcept(
36 noexcept(FastPimpl(std::declval<T&&>())))
37 : FastPimpl(std::move(*other)) {}
38
39 ~FastPimpl() noexcept(noexcept(std::destroy_at(std::declval<T*>())));
40
41 FastPimpl& operator=(const FastPimpl& rhs) noexcept(
42 noexcept(std::declval<T&>() = std::declval<const T&>()));
43
44 FastPimpl& operator=(FastPimpl&& rhs) noexcept(
45 noexcept(std::declval<T&>() = std::declval<T&&>()));
46
47 /**
48 * @brief Copy-assigns from a T instance.
49 * @param value Source value
50 * @return Reference to this instance
51 */
52 FastPimpl& operator=(const T& value) noexcept(
53 noexcept(std::declval<T&>() = std::declval<const T&>()));
54
55 /**
56 * @brief Move-assigns from a T instance.
57 * @param value Source value
58 * @return Reference to this instance
59 */
60 FastPimpl& operator=(T&& value) noexcept(
61 noexcept(std::declval<T&>() = std::declval<T&&>()));
62
63 T* operator->() noexcept { return Impl(); }
64 const T* operator->() const noexcept { return Impl(); }
65 T& operator*() noexcept { return *Impl(); }
66 const T& operator*() const noexcept { return *Impl(); }
67
68private:
69 static consteval void ValidateConstraints() noexcept;
70
71 T* Impl() noexcept {
72 return std::launder(reinterpret_cast<T*>(storage_.data()));
73 }
74
75 const T* Impl() const noexcept {
76 return std::launder(reinterpret_cast<const T*>(storage_.data()));
77 }
78
79 alignas(Alignment) std::array<std::byte, Size> storage_;
80};
81
82template <class T, size_t Size, size_t Alignment, bool RequireStrictMatch>
84 noexcept(std::destroy_at(std::declval<T*>()))) {
85 ValidateConstraints();
86 std::destroy_at(Impl());
87}
88
89template <class T, size_t Size, size_t Alignment, bool RequireStrictMatch>
91 const FastPimpl& rhs) noexcept(noexcept(std::declval<T&>() =
92 std::declval<const T&>()))
93 -> FastPimpl& {
94 if (this != &rhs) [[likely]] {
95 *Impl() = *rhs;
96 }
97 return *this;
98}
99
100template <class T, size_t Size, size_t Alignment, bool RequireStrictMatch>
102 FastPimpl&& rhs) noexcept(noexcept(std::declval<T&>() =
103 std::declval<T&&>())) -> FastPimpl& {
104 if (this != &rhs) [[likely]] {
105 *Impl() = std::move(*rhs);
106 }
107 return *this;
108}
109
110template <class T, size_t Size, size_t Alignment, bool RequireStrictMatch>
112 const T& value) noexcept(noexcept(std::declval<T&>() =
113 std::declval<const T&>()))
114 -> FastPimpl& {
115 if (Impl() != &value) [[likely]] {
116 *Impl() = value;
117 }
118 return *this;
119}
120
121template <class T, size_t Size, size_t Alignment, bool RequireStrictMatch>
123 T&& value) noexcept(noexcept(std::declval<T&>() = std::declval<T&&>()))
124 -> FastPimpl& {
125 if (Impl() != &value) [[likely]] {
126 *Impl() = std::move(value);
127 }
128 return *this;
129}
130
131template <class T, size_t Size, size_t Alignment, bool RequireStrictMatch>
132consteval void FastPimpl<T, Size, Alignment,
133 RequireStrictMatch>::ValidateConstraints() noexcept {
134 static_assert(Size >= sizeof(T), "FastPimpl: Size must be >= sizeof(T)");
135 static_assert(!RequireStrictMatch || Size == sizeof(T),
136 "FastPimpl: Strict match required but Size != sizeof(T)");
137
138 static_assert(Alignment % alignof(T) == 0,
139 "FastPimpl: Alignment must be a multiple of alignof(T)");
140 static_assert(!RequireStrictMatch || Alignment == alignof(T),
141 "FastPimpl: Strict match required but Alignment != alignof(T)");
142}
143
144} // namespace helios::utils
Implements pimpl idiom without dynamic memory allocation.
const T * operator->() const noexcept
FastPimpl(FastPimpl &&other) noexcept(noexcept(FastPimpl(std::declval< T && >())))
FastPimpl(const FastPimpl &other) noexcept(noexcept(FastPimpl(std::declval< const T & >())))
FastPimpl & operator=(const FastPimpl &rhs) noexcept(noexcept(std::declval< T & >()=std::declval< const T & >()))
T & operator*() noexcept
const T & operator*() const noexcept
~FastPimpl() noexcept(noexcept(std::destroy_at(std::declval< T * >())))
STL namespace.