1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Prerequisites/BsPrerequisitesUtil.h"
6#include "Error/BsException.h"
7
8namespace bs
9{
10 /** @addtogroup General
11 * @{
12 */
13
14 /**
15 * Represents one engine module. Essentially it is a specialized type of singleton. Module must be manually started up
16 * and shut down before and after use.
17 */
18 template <class T>
19 class Module
20 {
21 public:
22 /**
23 * Returns a reference to the module instance. Module has to have been started up first otherwise an exception will
24 * be thrown.
25 */
26 static T& instance()
27 {
28 if (!isStartedUp())
29 {
30 BS_EXCEPT(InternalErrorException,
31 "Trying to access a module but it hasn't been started up yet.");
32 }
33
34 if (isDestroyed())
35 {
36 BS_EXCEPT(InternalErrorException,
37 "Trying to access a destroyed module.");
38 }
39
40 return *_instance();
41 }
42
43 /**
44 * Returns a pointer to the module instance. Module has to have been started up first otherwise an exception will
45 * be thrown.
46 */
47 static T* instancePtr()
48 {
49 if (!isStartedUp())
50 {
51 BS_EXCEPT(InternalErrorException,
52 "Trying to access a module but it hasn't been started up yet.");
53 }
54
55 if (isDestroyed())
56 {
57 BS_EXCEPT(InternalErrorException,
58 "Trying to access a destroyed module.");
59 }
60
61 return _instance();
62 }
63
64 /** Constructs and starts the module using the specified parameters. */
65 template<class ...Args>
66 static void startUp(Args &&...args)
67 {
68 if (isStartedUp())
69 BS_EXCEPT(InternalErrorException, "Trying to start an already started module.");
70
71 _instance() = bs_new<T>(std::forward<Args>(args)...);
72 isStartedUp() = true;
73
74 ((Module*)_instance())->onStartUp();
75 }
76
77 /**
78 * Constructs and starts a specialized type of the module. Provided type must derive from type the Module is
79 * initialized with.
80 */
81 template<class SubType, class ...Args>
82 static void startUp(Args &&...args)
83 {
84 static_assert(std::is_base_of<T, SubType>::value, "Provided type is not derived from type the Module is initialized with.");
85
86 if (isStartedUp())
87 BS_EXCEPT(InternalErrorException, "Trying to start an already started module.");
88
89 _instance() = bs_new<SubType>(std::forward<Args>(args)...);
90 isStartedUp() = true;
91
92 ((Module*)_instance())->onStartUp();
93 }
94
95 /** Shuts down this module and frees any resources it is using. */
96 static void shutDown()
97 {
98 if (isDestroyed())
99 {
100 BS_EXCEPT(InternalErrorException,
101 "Trying to shut down an already shut down module.");
102 }
103
104 if (!isStartedUp())
105 {
106 BS_EXCEPT(InternalErrorException,
107 "Trying to shut down a module which was never started.");
108 }
109
110 ((Module*)_instance())->onShutDown();
111
112 bs_delete(_instance());
113 isDestroyed() = true;
114 }
115
116 /** Query if the module has been started. */
117 static bool isStarted()
118 {
119 return isStartedUp() && !isDestroyed();
120 }
121
122 protected:
123 Module() = default;
124
125 virtual ~Module() = default;
126
127 /*
128 * The notion of copying or moving a singleton is rather nonsensical.
129 */
130 Module(Module&&) = delete;
131 Module(const Module&) = delete;
132 Module& operator=(Module&&) = delete;
133 Module& operator=(const Module&) = delete;
134
135 /**
136 * Override if you want your module to be notified once it has been constructed and started.
137 *
138 * @note Useful when your module is polymorphic and you cannot perform some implementation specific
139 * initialization in constructor itself.
140 */
141 virtual void onStartUp() {}
142
143 /**
144 * Override if you want your module to be notified just before it is deleted.
145 *
146 * @note Useful when your module is polymorphic and you might want to perform some kind of clean up perhaps
147 * overriding that of a base class.
148 */
149 virtual void onShutDown() {}
150
151 /** Returns a singleton instance of this module. */
152 static T*& _instance()
153 {
154 static T* inst = nullptr;
155 return inst;
156 }
157
158 /**
159 * Checks has the Module been shut down.
160 *
161 * @note If module was never even started, this will return false.
162 */
163 static bool& isDestroyed()
164 {
165 static bool inst = false;
166 return inst;
167 }
168
169 /** Checks has the Module been started up. */
170 static bool& isStartedUp()
171 {
172 static bool inst = false;
173 return inst;
174 }
175 };
176
177 /** @} */
178}
179