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