/
imodule.h
443 lines (384 loc) · 14.2 KB
/
imodule.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
#pragma once
#include <memory>
#include <functional>
#include <cassert>
#include <sigc++/trackable.h>
#include <sigc++/signal.h>
#include <mutex>
#include <stdexcept>
#include <string>
#include <set>
#include <vector>
#include "itextstream.h"
#include "ilogwriter.h"
/**
* \defgroup module Module system
*/
/**
* greebo: Compatibility level: a number inlined into all the modules and returned
* by their RegisterableModule::getCompatibilityLevel() method.
*
* This number should be changed each time the set of module/plugin files (.so/.dll/.dylib)
* is modified, especially when files are going to be removed from a DarkRadiant release.
* The number will be checked by the ModuleRegistry against the internally stored one
* to detect outdated binaries and reject their registration.
*
* As long as no external module/plugin files are removed this number is safe to stay
* as it is. Keep this number compatible to std::size_t, i.e. unsigned.
*/
#define MODULE_COMPATIBILITY_LEVEL 20200419
// A function taking an error title and an error message string, invoked in debug builds
// for things like ASSERT_MESSAGE and ERROR_MESSAGE
typedef std::function<void (const std::string&, const std::string&)> ErrorHandlingFunction;
// This method holds a function pointer which can do some error display (like popups)
// Each module binary has its own copy of this, it's initialised in performDefaultInitialisation()
inline ErrorHandlingFunction& GlobalErrorHandler()
{
static ErrorHandlingFunction _func;
return _func;
}
/**
* Provider for various information that may be required by modules during
* initialisation.
*
* \ingroup module
*/
class ApplicationContext
{
public:
/**
* \brief
* Argument list type.
*/
typedef std::vector<std::string> ArgumentList;
/**
* Destructor
*/
virtual ~ApplicationContext() {}
/**
* Return the application path of the current Radiant instance.
*/
virtual std::string getApplicationPath() const = 0;
/**
* Return the application library path, containing modules and plugins.
*
* On Windows this is most likely the same as the application path. On
* Linux it might be a hard-coded path like /usr/lib/darkradiant, or a
* relocatable relative path like ../lib
*/
virtual std::string getLibraryPath() const = 0;
/**
* Return the toplevel path contaning runtime data files, such as the GL
* programs or UI descriptor files. On Windows this is the same as the
* application path, on Linux it is determined at compile-time but probably
* something like /usr/share/darkradiant.
*/
virtual std::string getRuntimeDataPath() const = 0;
/**
* Return the settings path of the current Radiant instance.
*/
virtual std::string getSettingsPath() const = 0;
/**
* Return the settings path of the current Radiant instance.
*/
virtual std::string getBitmapsPath() const = 0;
/// Return the path to HTML documentation files
virtual std::string getHTMLPath() const = 0;
/**
* \brief
* Return the list of command line arguments.
*/
virtual const ArgumentList& getCmdLineArgs() const = 0;
/**
* Return the reference to the application's output/error streams.
*/
virtual std::ostream& getOutputStream() const = 0;
virtual std::ostream& getErrorStream() const = 0;
virtual std::ostream& getWarningStream() const = 0;
// Provides a single mutex object which should be locked by client code
// before writing to the any of the above streams.
virtual std::mutex& getStreamLock() const = 0;
/**
* Retrieve a function pointer which can handle assertions and runtime errors
*/
virtual const ErrorHandlingFunction& getErrorHandlingFunction() const = 0;
};
/**
* Set of strings for module dependencies.
*/
typedef std::set<std::string> StringSet;
/**
* Interface class for modules. Each RegisterableModule can return its name and
* its set of dependencies.
*
* Note that this interface does NOT concern itself with the type (interface)
* of each individual module; it is up to the GlobalBlah() accessor function
* associated with each module to perform the required downcast to the known
* type.
*
* All RegisterableModules implement sigc::trackable, since they will often want
* to connect themselves to another module's signal(s).
*
* \ingroup module
*/
class RegisterableModule: public sigc::trackable
{
private:
const std::size_t _compatibilityLevel;
public:
RegisterableModule() :
_compatibilityLevel(MODULE_COMPATIBILITY_LEVEL)
{}
/**
* Destructor
*/
virtual ~RegisterableModule() {}
/**
* Return the name of this module. This must be globally unique across all
* modules; the modulesystem will throw a logic_error if two modules attempt
* to register themselves with the same name.
*/
virtual const std::string& getName() const = 0;
/**
* Return the set of dependencies for this module. The return value is a
* set of strings, each containing the unique name (as returned by
* getName()) of a module which must be initialised before this one.
*/
virtual const StringSet& getDependencies() const = 0;
/**
* Instruct this module to initialise itself. A RegisterableModule must NOT
* invoke any calls to other modules in its constructor, since at the point
* of construction the other modules will not have been initialised. Once
* all of the dependencies are processed by the ModuleRegistry, each module
* will have its initialiseModule() method called at the appropriate point.
*
* The ModuleRegistry guarantees that any modules named in the set of
* dependencies returned by getDependencies() will be initialised and ready
* for use at the time this method is invoked. Attempting to access a module
* which was NOT listed as a dependency is undefined.
*
* @param ctx
* The ApplicationContext of the running Radiant application.
*/
virtual void initialiseModule(const ApplicationContext& ctx) = 0;
/**
* Optional shutdown routine. Allows the module to de-register itself,
* shutdown windows, save stuff into the Registry and so on.
*
* All the modules are getting called one by one, all other modules
* are available until the last shutdownModule() call was invoked.
*/
virtual void shutdownModule() {
// Empty default implementation
}
// Internally queried by the ModuleRegistry. To protect against leftover
// binaries containing outdated moudles from being loaded and registered
// the compatibility level is compared with the one in the ModuleRegistry.
// Old modules with mismatching numbers will be rejected.
// Function is intentionally non-virtual and inlined.
std::size_t getCompatibilityLevel() const
{
return _compatibilityLevel;
}
};
/**
* Shared pointer typdef.
*/
typedef std::shared_ptr<RegisterableModule> RegisterableModulePtr;
/**
* Interface for the module registry. This is the owner and manager of all
* RegisterableModules defined in DLLs and the main binary.
*
* For obvious reasons, the ModuleRegistry itself is not a module, but a static
* object owned by the main binary and returned through a globally-accessible
* method.
*
* \ingroup module
*/
class IModuleRegistry
{
public:
/**
* Destructor
*/
virtual ~IModuleRegistry() {}
/**
* Register a RegisterableModule. The name and dependencies are retrieved
* through the appropriate RegisterableModule interface methods.
*
* This method does not cause the RegisterableModule to be initialised.
*/
virtual void registerModule(const RegisterableModulePtr& module) = 0;
/**
* Initialise all of the modules previously registered with
* registerModule() in the order required by their dependencies. This method
* is invoked once, at application startup, with any subsequent attempts
* to invoke this method throwing a logic_error.
*/
virtual void loadAndInitialiseModules() = 0;
/**
* All the RegisterableModule::shutdownModule() routines are getting
* called one by one. No modules are actually destroyed during the
* iteration is ongoing, so each module is guaranteed to exist.
*
* After the last shutdownModule() call has been invoked, the modules
* can be safely unloaded/destroyed.
*/
virtual void shutdownModules() = 0;
/**
* Retrieve the module associated with the provided unique name. If the
* named module is not found, an empty RegisterableModulePtr is returned
* (this allows modules to be optional).
*
* Note that the return value of this function is RegisterableModulePtr,
* which in itself is useless to application code. It is up to the accessor
* functions defined in each module interface (e.g. GlobalEntityCreator())
* to downcast the pointer to the appropriate type.
*/
virtual RegisterableModulePtr getModule(const std::string& name) const = 0;
/**
* Returns TRUE if the named module exists in the records.
*/
virtual bool moduleExists(const std::string& name) const = 0;
/**
* This retrieves a reference to the information structure ApplicationContext,
* which holds the AppPath, SettingsPath and references to the application
* streams. The latter can be used by modules to initialise their
* rMessage/globalErrorStreams().
*/
virtual const ApplicationContext& getApplicationContext() const = 0;
/**
* Invoked when all modules have been initialised.
*/
virtual sigc::signal<void> signal_allModulesInitialised() const = 0;
/**
* Progress function called during module loading and intialisation.
* The string value will carry a message about what is currently in progress.
* The float value passed to the signal indicates the overall progress and
* will be in the range [0.0f..1.0f].
*/
typedef sigc::signal<void, const std::string&, float> ProgressSignal;
virtual ProgressSignal signal_moduleInitialisationProgress() const = 0;
/**
* Invoked when all modules have been shut down (i.e. after shutdownModule()).
*/
virtual sigc::signal<void> signal_allModulesUninitialised() const = 0;
// The compatibility level this Registry instance was compiled against.
// Old module registrations will be rejected by the registry anyway,
// on top of that they can actively query this number from the registry
// to check whether they are being loaded into an incompatible binary.
virtual std::size_t getCompatibilityLevel() const = 0;
};
namespace module
{
/**
* \namespace module
* Types and functions implementing the module registry system.
*
* \ingroup module
*/
/** greebo: This is a container holding a reference to the registry.
* The getRegistry() symbol above is not exported to the
* modules in Win32 compiles. That's why this structure
* has to be initialised in the RegisterModule() routine.
*
* As soon as it's initialised, the module can access the ModuleRegistry
* with the routine GlobalModuleRegistry() below.
*/
class RegistryReference {
IModuleRegistry* _registry;
public:
RegistryReference() :
_registry(NULL)
{}
inline void setRegistry(IModuleRegistry& registry) {
_registry = ®istry;
}
inline IModuleRegistry& getRegistry() {
assert(_registry); // must not be NULL
return *_registry;
}
static RegistryReference& Instance() {
static RegistryReference _registryRef;
return _registryRef;
}
};
/**
* Global accessor method for the ModuleRegistry.
*/
inline IModuleRegistry& GlobalModuleRegistry()
{
return RegistryReference::Instance().getRegistry();
}
// Exception thrown if the module being loaded is incompatible with the main binary
class ModuleCompatibilityException :
public std::runtime_error
{
public:
ModuleCompatibilityException(const std::string& msg) :
std::runtime_error(msg)
{}
};
// greebo: This should be called once by each module at load time to initialise
// the OutputStreamHolders
inline void initialiseStreams(applog::ILogWriter& logWriter)
{
GlobalOutputStream().setStream(logWriter.getLogStream(applog::LogLevel::Standard));
GlobalWarningStream().setStream(logWriter.getLogStream(applog::LogLevel::Warning));
GlobalErrorStream().setStream(logWriter.getLogStream(applog::LogLevel::Error));
#ifndef NDEBUG
GlobalDebugStream().setStream(logWriter.getLogStream(applog::LogLevel::Verbose));
#endif
// Set up the mutex for thread-safe logging
GlobalOutputStream().setLock(logWriter.getStreamLock());
GlobalWarningStream().setLock(logWriter.getStreamLock());
GlobalErrorStream().setLock(logWriter.getStreamLock());
GlobalDebugStream().setLock(logWriter.getStreamLock());
}
// greebo: This should be called once by each module at load time to initialise
// the OutputStreamHolders
inline void initialiseStreams(const ApplicationContext& ctx)
{
GlobalOutputStream().setStream(ctx.getOutputStream());
GlobalWarningStream().setStream(ctx.getWarningStream());
GlobalErrorStream().setStream(ctx.getErrorStream());
#ifndef NDEBUG
GlobalDebugStream().setStream(ctx.getOutputStream());
#endif
// Set up the mutex for thread-safe logging
GlobalOutputStream().setLock(ctx.getStreamLock());
GlobalWarningStream().setLock(ctx.getStreamLock());
GlobalErrorStream().setLock(ctx.getStreamLock());
GlobalDebugStream().setLock(ctx.getStreamLock());
}
// Helper method initialising a few references and checking a module's
// compatibility level with the one reported by the ModuleRegistry
inline void performDefaultInitialisation(IModuleRegistry& registry)
{
if (registry.getCompatibilityLevel() != MODULE_COMPATIBILITY_LEVEL)
{
throw ModuleCompatibilityException("Compatibility level mismatch");
}
// Initialise the streams using the given application context
initialiseStreams(registry.getApplicationContext());
// Remember the reference to the ModuleRegistry
RegistryReference::Instance().setRegistry(registry);
// Set up the assertion handler
GlobalErrorHandler() = registry.getApplicationContext().getErrorHandlingFunction();
}
}
// Platform-specific definition which needs to be defined both
// in the plugins and the main binary.
#if defined(WIN32)
#if defined(_MSC_VER)
// In VC++ we use this to export symbols instead of using .def files
// Note: don't use __stdcall since this is adding stack bytes to the function name
#define DARKRADIANT_DLLEXPORT __declspec(dllexport)
#else
#define DARKRADIANT_DLLEXPORT
#endif
#elif defined(__APPLE__)
#define DARKRADIANT_DLLEXPORT __attribute__((visibility("default")))
#else
#define DARKRADIANT_DLLEXPORT
#endif