1#ifndef SEAD_DISPOSER_H_
2#define SEAD_DISPOSER_H_
3
4#include <basis/seadNew.h>
5#include <basis/seadRawPrint.h>
6#include <basis/seadTypes.h>
7#include <container/seadListImpl.h>
8
9namespace sead
10{
11class Heap;
12
13class IDisposer
14{
15public:
16 enum class HeapNullOption
17 {
18 // disposer_heap must not be nullptr for this option.
19 AlwaysUseSpecifiedHeap = 0,
20 UseSpecifiedOrContainHeap = 1,
21 DoNotAppendDisposerIfNoHeapSpecified = 2,
22 UseSpecifiedOrCurrentHeap = 3,
23 };
24
25 IDisposer();
26 explicit IDisposer(Heap* disposer_heap,
27 HeapNullOption option = HeapNullOption::UseSpecifiedOrCurrentHeap);
28 virtual ~IDisposer();
29
30 static u32 getListNodeOffset() { return offsetof(IDisposer, mListNode); }
31
32protected:
33 Heap* getDisposerHeap_() const { return mDisposerHeap; }
34
35private:
36 friend class Heap;
37
38 Heap* mDisposerHeap;
39 ListNode mListNode;
40};
41
42} // namespace sead
43
44#define SEAD_INSTANCE(CLASS) (CLASS::instance())
45
46#define SEAD_SINGLETON_DISPOSER(CLASS) \
47public: \
48 class SingletonDisposer_ : public sead::IDisposer \
49 { \
50 public: \
51 using sead::IDisposer::IDisposer; \
52 virtual ~SingletonDisposer_(); \
53 \
54 static SingletonDisposer_* sStaticDisposer; \
55 }; \
56 \
57 static CLASS* instance() { return sInstance; } \
58 static CLASS* createInstance(sead::Heap* heap); \
59 static void deleteInstance(); \
60 \
61 CLASS(const CLASS&) = delete; \
62 CLASS& operator=(const CLASS&) = delete; \
63 CLASS(CLASS&&) = delete; \
64 CLASS& operator=(CLASS&&) = delete; \
65 \
66protected: \
67 static CLASS* sInstance; \
68 \
69 friend class SingletonDisposer_; \
70 \
71 /* FIXME: this should just use std::aligned_storage_t with a proper alignment value. */ \
72 u32 mSingletonDisposerBuf_[sizeof(SingletonDisposer_) / sizeof(u32)];
73
74#define SEAD_CREATE_SINGLETON_INSTANCE(CLASS) \
75 CLASS* CLASS::createInstance(sead::Heap* heap) \
76 { \
77 if (!sInstance) \
78 { \
79 auto* buffer = new (heap, alignof(CLASS)) u8[sizeof(CLASS)]; \
80 SEAD_ASSERT_MSG(!SingletonDisposer_::sStaticDisposer, "Create Singleton Twice (%s).", \
81 #CLASS); \
82 auto* disposer_buffer = buffer + offsetof(CLASS, mSingletonDisposerBuf_); \
83 \
84 /* FIXME: do this after creating the instance to ensure the disposer is not clobbered \
85 * by CLASS's constructor */ \
86 SingletonDisposer_::sStaticDisposer = new (disposer_buffer) SingletonDisposer_(heap); \
87 /* Note: When compiling as C++03 (or a newer standard), this must not be `new CLASS()` \
88 * as that will value initialize (C++17 [dcl.init]/11), which eventually leads to zero \
89 * initialisation when CLASS has no user-provided constructor (C++17 [dcl.init]/8). \
90 * This is dangerous because the singleton disposer would get clobbered. \
91 */ \
92 sInstance = new (buffer) CLASS; \
93 } \
94 else \
95 { \
96 SEAD_ASSERT_MSG(false, "Create Singleton Twice (%s) : addr %p", #CLASS, sInstance); \
97 } \
98 \
99 return CLASS::sInstance; \
100 }
101
102#define SEAD_DELETE_SINGLETON_INSTANCE(CLASS) \
103 void CLASS::deleteInstance() \
104 { \
105 SingletonDisposer_* staticDisposer = SingletonDisposer_::sStaticDisposer; \
106 if (SingletonDisposer_::sStaticDisposer != NULL) \
107 { \
108 SingletonDisposer_::sStaticDisposer = NULL; \
109 staticDisposer->~SingletonDisposer_(); \
110 \
111 delete sInstance; \
112 \
113 sInstance = NULL; \
114 } \
115 }
116
117#define SEAD_SINGLETON_DISPOSER_IMPL(CLASS) \
118 CLASS::SingletonDisposer_::~SingletonDisposer_() \
119 { \
120 if (this == sStaticDisposer) \
121 { \
122 sStaticDisposer = nullptr; \
123 CLASS::sInstance->~CLASS(); \
124 CLASS::sInstance = nullptr; \
125 } \
126 } \
127 SEAD_CREATE_SINGLETON_INSTANCE(CLASS) \
128 SEAD_DELETE_SINGLETON_INSTANCE(CLASS) \
129 CLASS* CLASS::sInstance = NULL; \
130 CLASS::SingletonDisposer_* CLASS::SingletonDisposer_::sStaticDisposer = NULL;
131
132#endif // SEAD_DISPOSER_H_
133