CeresEngine 0.2.0
A game development framework
Loading...
Searching...
No Matches
Poly.hpp
Go to the documentation of this file.
1//
2// CeresEngine - A game development framework
3//
4// Created by Rogiel Sulzbach.
5// Copyright (c) 2018-2022 Rogiel Sulzbach. All rights reserved.
6//
7
8#pragma once
9
12
13#include <memory>
14#include <type_traits>
15
16#ifndef NDEBUG
17#include <cstring>
18#endif
19
20namespace CeresEngine {
21
22#define CE_POLY_TMPL(T) typename T, size_t T##SmallSize, bool T##Copyable, typename T##BaseType
23#define CE_POLY_TYPE(T) Poly<T, T##SmallSize, T##Copyable, T##BaseType>
24
25 namespace impl {
28 template<typename T> struct PolyImplementation {
30 T* (*get)(void* raw);
31
33 void (*copy)(void* lhs, const void* rhs);
34
36 void (*move)(void* lhs, void* rhs);
37
39 void (*destroy)(void* raw);
40 };
41 }
42
57 template<typename T, size_t SmallSize = sizeof(int*) * 4, bool Copyable = true, typename BaseType = T> class Poly {
58 template<typename, size_t, bool, typename> friend class Poly;
59
60 private:
62 static_assert(SmallSize >= sizeof(void*), "SmallSize must be at least the size of a single pointer.");
63
66
69
70 public:
72 template<typename U> static constexpr inline bool isSmall = sizeof(U) <= SmallSize&& std::is_move_constructible_v<U>;
73
74 private:
76 template<typename TT> static const Implementation* create() {
77 if constexpr(isSmall<TT>) {
78 // For small objects, we can store the actual object
79 // directly in the raw memory.
80 static const Implementation implementation{
81 .get = [](void* const ptr) -> BaseType* { return reinterpret_cast<TT*>(ptr); },
82 .copy = []() -> decltype(Implementation::copy) {
83 if constexpr(std::is_copy_constructible_v<TT>) {
84 return [](void* const destination, const void* source) {
85 new(destination) TT(*reinterpret_cast<const TT*>(source));
86 };
87 }
88 return nullptr;
89 }(),
90 .move = []() -> decltype(Implementation::move) {
91 if constexpr(std::is_move_constructible_v<TT>) {
92 return [](void* const destination, void* const source) {
93 new(destination) TT(std::move(*reinterpret_cast<TT*>(source)));
94 };
95 }
96 return nullptr;
97 }(),
98 .destroy = [](void* const ptr) { reinterpret_cast<TT*>(ptr)->~TT(); },
99 };
100 return &implementation;
101 } else {
102 // However, for large objects we need to heap allocate and
103 // only store a pointer in to the raw memory.
104 static const Implementation implementation{
105 .get = [](void* const ptr) -> BaseType* { return *reinterpret_cast<TT**>(ptr); },
106 .copy = []() -> decltype(Implementation::copy) {
107 if constexpr(std::is_copy_constructible_v<TT>) {
108 return [](void* const destination, const void* const source) {
109 *reinterpret_cast<TT**>(destination) = new TT(**reinterpret_cast<const TT* const*>(source));
110 };
111 }
112 return nullptr;
113 }(),
114 .move =
115 [](void* const destination, void* const source) { //
116 *reinterpret_cast<TT**>(destination) = std::exchange(*reinterpret_cast<TT**>(source), nullptr);
117 },
118 .destroy = [](void* const ptr) { delete *reinterpret_cast<TT**>(ptr); },
119 };
120 return &implementation;
121 }
122 }
123
126 static const Implementation* getReference() {
127 static const Implementation implementation{
128 .get = [](void* const ptr) -> BaseType* { return *reinterpret_cast<T**>(ptr); },
129 .copy = [](void* const dst, const void* const src) { *reinterpret_cast<T**>(dst) = *reinterpret_cast<T* const*>(src); },
130 .move = [](void* const dst, void* const src) { *reinterpret_cast<T**>(dst) = *reinterpret_cast<T**>(src); },
131 .destroy = [](void* const ptr) { *reinterpret_cast<T**>(ptr) = nullptr; },
132 };
133 return &implementation;
134 }
135
136 public:
138 [[nodiscard]] bool isCopyable() const noexcept requires(Copyable) { return mImplementation == nullptr || mImplementation->copy != nullptr; }
139
142 Poly(const Poly& other) requires(Copyable) : mImplementation(other.mImplementation) {
143 CE_ASSERT(other.isCopyable());
144
145 if(mImplementation == nullptr) {
146 return;
147 }
148
149 CE_ASSERT(mImplementation->copy != nullptr);
150 mImplementation->copy(mRaw, other.mRaw);
151 }
152
156 Poly& operator=(const Poly& other) requires(Copyable) { // NOLINT(bugprone-unhandled-self-assignment,cert-oop54-cpp)
157 CE_ASSERT(other.isCopyable());
158
159 reset();
160 CE_ASSERT(mImplementation == nullptr);
161
162 mImplementation = other.mImplementation;
163 if(mImplementation != nullptr) {
164 CE_ASSERT(mImplementation->copy != nullptr);
165 mImplementation->copy(mRaw, other.mRaw);
166 }
167
168 return *this;
169 }
170
172 [[nodiscard]] bool isMovable() const noexcept { return mImplementation == nullptr || mImplementation->move != nullptr; }
173
176 Poly(Poly&& other) noexcept : mImplementation(other.mImplementation) {
177 CE_ASSERT(other.isMovable());
178
179 if(mImplementation == nullptr) {
180 return;
181 }
182
183 CE_ASSERT(mImplementation->move != nullptr);
184 mImplementation->move(mRaw, other.mRaw);
185 }
186
190 Poly& operator=(Poly&& other) { // NOLINT
191 CE_ASSERT(other.isMovable());
192
193 reset();
194 CE_ASSERT(mImplementation == nullptr);
195
196 mImplementation = other.mImplementation;
197 if(mImplementation != nullptr) {
198 CE_ASSERT(mImplementation->move != nullptr);
199 mImplementation->move(mRaw, other.mRaw);
200 }
201
202 return *this;
203 }
204
206 Poly(std::nullptr_t) noexcept : mImplementation(nullptr) {} // NOLINT
207
210 Poly& operator=(std::nullptr_t) noexcept {
211 reset();
212 return *this;
213 }
214
217
218 private:
222 template<typename TT> struct ConstructTag {};
223
229 template<typename TT, typename... Args>
230 explicit Poly(ConstructTag<TT>, Args&&... args) requires(std::is_convertible_v<TT*, T*>&& std::is_constructible_v<TT, Args&&...>) {
231 emplace<TT>(std::forward<Args>(args)...);
232 }
233
234 public:
236 template<typename TT = T> Poly() noexcept requires(std::is_default_constructible_v<TT>) : Poly(ConstructTag<TT>{}) {}
242 template<typename TT, typename... Args>
243 explicit Poly(std::in_place_type_t<TT>, Args&&... args) requires(std::is_convertible_v<TT*, T*>&& std::is_constructible_v<TT, Args&&...>) {
244 emplace<TT>(std::forward<Args>(args)...);
245 }
246
251 template<typename... Args>
252 explicit Poly(Args&&... args) requires(std::is_constructible_v<T, Args...>) : Poly(ConstructTag<T>{}, std::forward<Args>(args)...) {}
253
257 template<typename TT>
258 Poly(TT object) requires(std::is_convertible_v<TT*, T*> && Copyable) // NOLINT
259 : Poly(ConstructTag<TT>{}, std::forward<TT>(object)) {}
260
264 template<typename TT>
265 Poly(TT&& object) requires(std::is_convertible_v<TT*, T*> && !Copyable) { // NOLINT
266 emplace<TT>(std::move(object));
267 }
268
273 CE_EXPLICIT(false) Poly(T* const instance) {
275 CE_ASSERT(mImplementation != nullptr);
276
277 *reinterpret_cast<T**>(mRaw) = instance;
278 }
279
284 template<typename TT, typename... Args> TT& emplace(Args&&... args) requires std::is_constructible_v<TT, Args...> {
285 reset();
286 CE_ASSERT(mImplementation == nullptr);
287
289 CE_ASSERT(mImplementation != nullptr);
290
291 using SmallType = TT;
292 using LargeType = TT*;
293
294 if constexpr(isSmall<TT>) {
295 new(mRaw) SmallType(std::forward<Args>(args)...);
296 } else {
297 TT* const pointer = new TT(std::forward<Args>(args)...);
298 new(mRaw) LargeType(pointer);
299 }
300
301 return static_cast<TT&>(*get());
302 }
303
304 template<typename TT> TT& emplace(TT&& instance) {
305 reset();
306 CE_ASSERT(mImplementation == nullptr);
307
309 CE_ASSERT(mImplementation != nullptr);
310
311 using SmallType = TT;
312 using LargeType = TT*;
313
314 if constexpr(isSmall<TT>) {
315 new(mRaw) SmallType(std::move(instance));
316 } else {
317 TT* const pointer = new TT(std::move(instance));
318 new(mRaw) LargeType(pointer);
319 }
320
321 return static_cast<TT&>(*get());
322 }
323
328 template<typename U> requires(Copyable)
330 CE_ASSERT(other.isCopyable());
331 if(mImplementation == nullptr) {
332 return;
333 }
334
335 CE_ASSERT(mImplementation->copy != nullptr);
336 mImplementation->copy(mRaw, other.mRaw);
337 }
338
343 template<typename U>
345 CE_ASSERT(other.isMovable());
346 if(mImplementation == nullptr) {
347 return;
348 }
349
350 CE_ASSERT(mImplementation->move != nullptr);
351 mImplementation->move(mRaw, other.mRaw);
352 }
353
354 public: // Public interface
357 [[nodiscard]] bool valid() const { return get() != nullptr; }
358
360 [[nodiscard]] bool empty() const { return valid(); }
361
364 [[nodiscard]] explicit operator bool() const { return valid(); }
365
367 [[nodiscard]] T* get() { return mImplementation == nullptr ? nullptr : dynamic_cast<T*>(mImplementation->get(mRaw)); }
368
370 [[nodiscard]] const T* get() const { return mImplementation == nullptr ? nullptr : dynamic_cast<const T*>(mImplementation->get(mRaw)); }
371
373 [[nodiscard]] T* operator->() { return get(); }
374
376 [[nodiscard]] const T* operator->() const { return get(); }
377
379 [[nodiscard]] const T& operator*() const& {
380 CE_ASSERT(valid());
381 return *get();
382 }
383
386 CE_ASSERT(valid());
387 return *get();
388 }
389
391 [[nodiscard]] T&& operator*() && {
392 CE_ASSERT(valid());
393 return std::move(*get());
394 }
395
396 public: // Type casting
398 [[nodiscard]] const std::type_info& getTypeID() const {
399 CE_ASSERT(valid());
400 return typeid(*get());
401 }
402
406 template<typename TT> [[nodiscard]] bool is() const requires std::is_convertible_v<TT*, T*> { return dynamic_cast<const TT*>(get()) != nullptr; }
407
412 template<typename TT> TT* being() requires std::is_convertible_v<TT*, T*> { return dynamic_cast<TT*>(get()); }
413
415 template<typename TT> const TT* being() const requires std::is_convertible_v<TT*, T*> { return dynamic_cast<const TT*>(get()); }
416
421 template<typename TT> TT& as() & requires std::is_convertible_v<TT*, T*> {
422 CE_ASSERT(valid());
424 return static_cast<TT&>(*get());
425 }
426
428 template<typename TT> TT&& as() && requires std::is_convertible_v<TT*, T*> {
429 CE_ASSERT(valid());
431 return static_cast<TT&&>(*get());
432 }
433
435 template<typename TT> const TT& as() const& requires std::is_convertible_v<TT*, T*> {
436 CE_ASSERT(valid());
438 return static_cast<const TT&>(*get());
439 }
440
441 private:
444 void reset() {
445 if(mImplementation == nullptr) {
446 return;
447 }
448
449 CE_ASSERT(mImplementation->destroy != nullptr);
451 mImplementation = nullptr;
452#ifndef NDEBUG
453 std::memset(mRaw, 0x00, sizeof(mRaw));
454#endif
455 }
456 };
457
462 template<CE_POLY_TMPL(T), CE_POLY_TMPL(U)> bool operator==(const CE_POLY_TYPE(T) & lhs, const CE_POLY_TYPE(U) & rhs) noexcept {
463 if(lhs.get() == nullptr) {
464 return false;
465 }
466 if(rhs.get() == nullptr) {
467 return false;
468 }
469 return *(lhs.get()) == *(rhs.get());
470 }
471
476 template<CE_POLY_TMPL(T), typename U> bool operator==(const CE_POLY_TYPE(T) & lhs, const U& rhs) noexcept {
477 if(lhs.get() == nullptr) {
478 return false;
479 }
480 return *(lhs.get()) == rhs;
481 }
482
487 template<typename U, CE_POLY_TMPL(T)> bool operator==(const U& lhs, const CE_POLY_TYPE(T) & rhs) noexcept {
488 if(rhs.get() == nullptr) {
489 return false;
490 }
491 return lhs == *(rhs.get());
492 }
493
498 template<CE_POLY_TMPL(T), CE_POLY_TMPL(U)> bool operator!=(const CE_POLY_TYPE(T) & lhs, const CE_POLY_TYPE(U) & rhs) noexcept {
499 if(lhs.get() == nullptr) {
500 return true;
501 }
502 if(rhs.get() == nullptr) {
503 return true;
504 }
505 return *(lhs.get()) != *(rhs.get());
506 }
507
512 template<CE_POLY_TMPL(T), typename U> bool operator!=(const CE_POLY_TYPE(T) & lhs, const U& rhs) noexcept {
513 if(lhs.get() == nullptr) {
514 return true;
515 }
516 return *(lhs.get()) != rhs;
517 }
518
523 template<typename U, CE_POLY_TMPL(T)> bool operator!=(const U& lhs, const CE_POLY_TYPE(T) & rhs) noexcept {
524 if(rhs.get() == nullptr) {
525 return true;
526 }
527 return lhs != *(rhs.get());
528 }
529
534 template<CE_POLY_TMPL(T), CE_POLY_TMPL(U)> bool operator>(const CE_POLY_TYPE(T) & lhs, const CE_POLY_TYPE(U) & rhs) noexcept {
535 if(lhs.get() == nullptr) {
536 return false;
537 }
538 if(rhs.get() == nullptr) {
539 return false;
540 }
541 return *(lhs.get()) > *(rhs.get());
542 }
543
548 template<CE_POLY_TMPL(T), typename U> bool operator>(const CE_POLY_TYPE(T) & lhs, const U& rhs) noexcept {
549 if(lhs.get() == nullptr) {
550 return false;
551 }
552 return *(lhs.get()) > rhs;
553 }
554
559 template<typename U, CE_POLY_TMPL(T)> bool operator>(const U& lhs, const CE_POLY_TYPE(T) & rhs) noexcept {
560 if(rhs.get() == nullptr) {
561 return false;
562 }
563 return lhs > *(rhs.get());
564 }
565
570 template<CE_POLY_TMPL(T), CE_POLY_TMPL(U)> bool operator>=(const CE_POLY_TYPE(T) & lhs, const CE_POLY_TYPE(U) & rhs) noexcept {
571 if(lhs.get() == nullptr) {
572 return false;
573 }
574 if(rhs.get() == nullptr) {
575 return false;
576 }
577 return *(lhs.get()) >= *(rhs.get());
578 }
579
584 template<CE_POLY_TMPL(T), typename U> bool operator>=(const CE_POLY_TYPE(T) & lhs, const U& rhs) noexcept {
585 if(lhs.get() == nullptr) {
586 return false;
587 }
588 return *(lhs.get()) >= rhs;
589 }
590
595 template<typename U, CE_POLY_TMPL(T)> bool operator>=(const U& lhs, const CE_POLY_TYPE(T) & rhs) noexcept {
596 if(rhs.get() == nullptr) {
597 return false;
598 }
599 return lhs >= *(rhs.get());
600 }
601
606 template<CE_POLY_TMPL(T), CE_POLY_TMPL(U)> bool operator<(const CE_POLY_TYPE(T) & lhs, const CE_POLY_TYPE(U) & rhs) noexcept {
607 if(lhs.get() == nullptr) {
608 return false;
609 }
610 if(rhs.get() == nullptr) {
611 return false;
612 }
613 return *(lhs.get()) < *(rhs.get());
614 }
615
620 template<CE_POLY_TMPL(T), typename U> bool operator<(const CE_POLY_TYPE(T) & lhs, const U& rhs) noexcept {
621 if(lhs.get() == nullptr) {
622 return false;
623 }
624 return *(lhs.get()) < rhs;
625 }
626
631 template<typename U, CE_POLY_TMPL(T)> bool operator<(const U& lhs, const CE_POLY_TYPE(T) & rhs) noexcept {
632 if(rhs.get() == nullptr) {
633 return false;
634 }
635 return lhs < *(rhs.get());
636 }
637
642 template<CE_POLY_TMPL(T), CE_POLY_TMPL(U)> bool operator<=(const CE_POLY_TYPE(T) & lhs, const CE_POLY_TYPE(U) & rhs) noexcept {
643 if(lhs.get() == nullptr) {
644 return false;
645 }
646 if(rhs.get() == nullptr) {
647 return false;
648 }
649 return *(lhs.get()) <= *(rhs.get());
650 }
651
656 template<CE_POLY_TMPL(T), typename U> bool operator<=(const CE_POLY_TYPE(T) & lhs, const U& rhs) noexcept {
657 if(lhs.get() == nullptr) {
658 return false;
659 }
660 return *(lhs.get()) <= rhs;
661 }
662
667 template<typename U, CE_POLY_TMPL(T)> bool operator<=(const U& lhs, const CE_POLY_TYPE(T) & rhs) noexcept {
668 if(rhs.get() == nullptr) {
669 return false;
670 }
671 return lhs <= *(rhs.get());
672 }
673
677 template<CE_POLY_TMPL(T)> bool operator==(const CE_POLY_TYPE(T) & lhs, std::nullptr_t) noexcept { return lhs.get() == nullptr; }
678
682 template<CE_POLY_TMPL(T)> bool operator!=(const CE_POLY_TYPE(T) & lhs, std::nullptr_t) noexcept { return lhs.get() != nullptr; }
683
684#undef CE_POLY_TMPL
685#undef CE_POLY_TYPE
686
687} // namespace CeresEngine
#define CE_ASSERT(...)
Definition Macros.hpp:323
#define CE_ASSERT_DEBUG(...)
Definition Macros.hpp:334
#define CE_EXPLICIT(EXPR)
Definition Macros.hpp:413
#define CE_POLY_TYPE(T)
Definition Poly.hpp:23
A pointer type that has value semantics.
Definition Poly.hpp:57
TT * being()
Safely casts the hold type to TT.
Definition Poly.hpp:412
static const Implementation * create()
Creates a new implementation for the concrete type TT.
Definition Poly.hpp:76
bool valid() const
Checks if the pointer has a valid object stored in it.
Definition Poly.hpp:357
T & operator*() &
&
Definition Poly.hpp:385
static constexpr bool isSmall
true if an object of type U will not cause an allocation.
Definition Poly.hpp:72
Poly() noexcept
Creates a new empty Poly.
Definition Poly.hpp:236
void reset()
Resets the stored object into an empty state.
Definition Poly.hpp:444
static const Implementation * getReference()
Gets the implementation table for referencing the polymorphic object as a reference.
Definition Poly.hpp:126
Poly & operator=(const Poly &other)
Assigns a Poly by copying an existing one.
Definition Poly.hpp:156
Byte mRaw[SmallSize]
The underlying storage memory.
Definition Poly.hpp:65
bool empty() const
Checks if the pointer has a valid object stored in it.
Definition Poly.hpp:360
TT & as() &
Casts the hold type to TT.
Definition Poly.hpp:421
TT & emplace(TT &&instance)
Definition Poly.hpp:304
Poly(const Poly< U, SmallSize, Copyable, BaseType > &other)
Creates a new Poly instance by referencing another.
Definition Poly.hpp:329
const Implementation * mImplementation
A Implementation that contains implementation for copy, move and destroy.
Definition Poly.hpp:68
const T & operator*() const &
Definition Poly.hpp:379
~Poly() noexcept
Destroys the Poly and destroys the object is present.
Definition Poly.hpp:216
Poly(std::nullptr_t) noexcept
Creates a new empty Poly.
Definition Poly.hpp:206
T * operator->()
Definition Poly.hpp:373
Poly(TT object)
Creates a new Poly that holds a copy constructed object of type TT.
Definition Poly.hpp:258
Poly(std::in_place_type_t< TT >, Args &&... args)
Creates a new Poly that holds a newly constructed object of type TT.
Definition Poly.hpp:243
TT && as() &&
Casts the hold type to TT.
Definition Poly.hpp:428
T * get()
Definition Poly.hpp:367
bool isCopyable() const noexcept
Checks if the Poly copy constructor can be safely called.
Definition Poly.hpp:138
const T * get() const
Definition Poly.hpp:370
Poly(TT &&object)
Creates a new Poly that holds a copy constructed object of type TT.
Definition Poly.hpp:265
bool is() const
Checks if the hold object is of type TT.
Definition Poly.hpp:406
Poly & operator=(std::nullptr_t) noexcept
Assigns the Poly a nullptr value.
Definition Poly.hpp:210
Poly(ConstructTag< TT >, Args &&... args)
Creates a new Poly that holds a newly constructed object of type TT.
Definition Poly.hpp:230
const T * operator->() const
Definition Poly.hpp:376
const TT * being() const
Safely casts the hold type to TT.
Definition Poly.hpp:415
bool isMovable() const noexcept
Checks if the Poly move constructor can be safely called.
Definition Poly.hpp:172
const TT & as() const &
Casts the hold type to TT.
Definition Poly.hpp:435
Poly & operator=(Poly &&other)
Assigns a Poly by moving an existing one.
Definition Poly.hpp:190
T && operator*() &&
&
Definition Poly.hpp:391
Poly(Args &&... args)
Creates a new Poly that holds a newly constructed object of type T.
Definition Poly.hpp:252
const std::type_info & getTypeID() const
Definition Poly.hpp:398
TT & emplace(Args &&... args)
Emplace a new object of type TT into the Poly.
Definition Poly.hpp:284
Poly(Poly &&other) noexcept
Creates a new Poly by moving an existing one.
Definition Poly.hpp:176
Poly(const Poly &other)
Creates a new Poly by copying an existing one.
Definition Poly.hpp:142
Definition Application.hpp:19
Byte
Definition DataTypes.hpp:40
constexpr bool operator<(const Optional< A > &lhs, const Optional< B > &rhs)
Definition Optional.hpp:162
auto move(Vector3 position)
Moves a entity to the given position.
Definition Helpers.hpp:22
bool operator!=(const ShortAllocator< T, N, A1 > &x, const ShortAllocator< U, M, A2 > &y) noexcept
Definition Allocator.hpp:416
constexpr bool operator>=(const Optional< A > &lhs, const Optional< B > &rhs)
Definition Optional.hpp:157
bool operator==(const ShortAllocator< T, N, A1 > &x, const ShortAllocator< U, M, A2 > &y) noexcept
Definition Allocator.hpp:411
constexpr bool operator>(const Optional< A > &lhs, const Optional< B > &rhs)
Definition Optional.hpp:152
constexpr size_t hash(const T &v)
Generates a hash for the provided type.
Definition Hash.hpp:25
constexpr bool operator<=(const Optional< A > &lhs, const Optional< B > &rhs)
Definition Optional.hpp:167
Definition Span.hpp:668
A tag used for tagged dispatching the object constructing constructor.
Definition Poly.hpp:222
A struct that contains the implementation for each concrete type get, copy, move and destroy function...
Definition Poly.hpp:28
void(* move)(void *lhs, void *rhs)
Moves the object at rhs to lhs.
Definition Poly.hpp:36
void(* copy)(void *lhs, const void *rhs)
Copies the object at rhs to lhs.
Definition Poly.hpp:33
T *(* get)(void *raw)
Extracts a function pointer from the raw data.
Definition Poly.hpp:30
void(* destroy)(void *raw)
Destroys the object stored at the given raw location.
Definition Poly.hpp:39