1 /*
2 * ComponentManager.h
3 * ------------------
4 * Purpose: Manages loading of optional components.
5 * Notes : (currently none)
6 * Authors: Joern Heusipp
7 * OpenMPT Devs
8 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
9 */
10
11 #pragma once
12
13 #include "openmpt/all/BuildSettings.hpp"
14
15 #include "mpt/mutex/mutex.hpp"
16
17 #include <map>
18 #include <vector>
19 #include "../common/misc_util.h"
20 #if defined(MODPLUG_TRACKER)
21 #include "../misc/mptLibrary.h"
22 #endif
23
24
25 OPENMPT_NAMESPACE_BEGIN
26
27
28 enum ComponentType
29 {
30 ComponentTypeUnknown = 0,
31 ComponentTypeBuiltin, // PortAudio
32 ComponentTypeSystem, // mf.dll
33 ComponentTypeSystemInstallable, // acm mp3 codec
34 ComponentTypeBundled, // libsoundtouch
35 ComponentTypeForeign, // libmp3lame
36 };
37
38
39 class ComponentFactoryBase;
40
41
42 class IComponent
43 {
44
45 friend class ComponentFactoryBase;
46
47 protected:
48
49 IComponent() = default;
50
51 public:
52
53 virtual ~IComponent() = default;
54
55 public:
56
57 virtual ComponentType GetType() const = 0;
58
59 virtual bool IsInitialized() const = 0; // Initialize() has been called
60 virtual bool IsAvailable() const = 0; // Initialize() has been successfull
61 virtual mpt::ustring GetVersion() const = 0;
62
63 virtual void Initialize() = 0; // try to load the component
64
65 };
66
67
68 class ComponentBase
69 : public IComponent
70 {
71
72 private:
73
74 ComponentType m_Type;
75
76 bool m_Initialized;
77 bool m_Available;
78
79 protected:
80
81 ComponentBase(ComponentType type);
82
83 public:
84
85 ~ComponentBase() override;
86
87 protected:
88
89 void SetInitialized();
90 void SetAvailable();
91
92 public:
93
94 ComponentType GetType() const override;
95 bool IsInitialized() const override;
96 bool IsAvailable() const override;
97
98 mpt::ustring GetVersion() const override;
99
100 public:
101
102 void Initialize() override;
103
104 protected:
105
106 virtual bool DoInitialize() = 0;
107
108 };
109
110
111 class ComponentBuiltin : public ComponentBase
112 {
113 public:
ComponentBuiltin()114 ComponentBuiltin()
115 : ComponentBase(ComponentTypeBuiltin)
116 {
117 return;
118 }
DoInitialize()119 bool DoInitialize() override
120 {
121 return true;
122 }
123 };
124
125
126 #define MPT_GLOBAL_BIND(lib, name) name = &::name;
127
128
129 #if defined(MODPLUG_TRACKER)
130
131
132 class ComponentLibrary
133 : public ComponentBase
134 {
135
136 private:
137
138 typedef std::map<std::string, mpt::Library> TLibraryMap;
139 TLibraryMap m_Libraries;
140
141 bool m_BindFailed;
142
143 protected:
144
145 ComponentLibrary(ComponentType type);
146
147 public:
148
149 virtual ~ComponentLibrary();
150
151 protected:
152
153 bool AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath);
154 void ClearLibraries();
155 void SetBindFailed();
156 void ClearBindFailed();
157 bool HasBindFailed() const;
158
159 public:
160
161 virtual mpt::Library GetLibrary(const std::string &libName) const;
162
163 template <typename Tfunc>
Bind(Tfunc * & f,const std::string & libName,const std::string & symbol)164 bool Bind(Tfunc * & f, const std::string &libName, const std::string &symbol) const
165 {
166 return GetLibrary(libName).Bind(f, symbol);
167 }
168
169 protected:
170
171 bool DoInitialize() override = 0;
172
173 };
174
175
176 #define MPT_COMPONENT_BIND(libName, func) do { if(!Bind( func , libName , #func )) { SetBindFailed(); } } while(0)
177 #define MPT_COMPONENT_BIND_OPTIONAL(libName, func) Bind( func , libName , #func )
178 #define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) do { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } while(0)
179 #define MPT_COMPONENT_BIND_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol )
180
181 #if MPT_OS_WINDOWS
182 #ifdef UNICODE
183 #define MPT_COMPONENT_BINDWIN_SUFFIX "W"
184 #else
185 #define MPT_COMPONENT_BINDWIN_SUFFIX "A"
186 #endif
187 #define MPT_COMPONENT_BINDWIN(libName, func) do { if(!Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } while(0)
188 #define MPT_COMPONENT_BINDWIN_OPTIONAL(libName, func) Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )
189 #define MPT_COMPONENT_BINDWIN_SYMBOL(libName, symbol, func) do { if(!Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } while(0)
190 #define MPT_COMPONENT_BINDWIN_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )
191 #endif
192
193
194 class ComponentSystemDLL : public ComponentLibrary
195 {
196 private:
197 mpt::PathString m_BaseName;
198 public:
ComponentSystemDLL(const mpt::PathString & baseName)199 ComponentSystemDLL(const mpt::PathString &baseName)
200 : ComponentLibrary(ComponentTypeSystem)
201 , m_BaseName(baseName)
202 {
203 return;
204 }
DoInitialize()205 bool DoInitialize() override
206 {
207 AddLibrary(m_BaseName.ToUTF8(), mpt::LibraryPath::System(m_BaseName));
208 return GetLibrary(m_BaseName.ToUTF8()).IsValid();
209 }
210 };
211
212
213 class ComponentBundledDLL : public ComponentLibrary
214 {
215 private:
216 mpt::PathString m_FullName;
217 public:
ComponentBundledDLL(const mpt::PathString & fullName)218 ComponentBundledDLL(const mpt::PathString &fullName)
219 : ComponentLibrary(ComponentTypeBundled)
220 , m_FullName(fullName)
221 {
222 return;
223 }
DoInitialize()224 bool DoInitialize() override
225 {
226 AddLibrary(m_FullName.ToUTF8(), mpt::LibraryPath::AppFullName(m_FullName));
227 return GetLibrary(m_FullName.ToUTF8()).IsValid();
228 }
229 };
230
231
232 #endif // MODPLUG_TRACKER
233
234
235 #if MPT_COMPONENT_MANAGER
236
237
238 class ComponentManager;
239
240 typedef std::shared_ptr<IComponent> (*ComponentFactoryMethod)(ComponentManager &componentManager);
241
242
243 class IComponentFactory
244 {
245 protected:
246 IComponentFactory() = default;
247 public:
248 virtual ~IComponentFactory() = default;
249 public:
250 virtual std::string GetID() const = 0;
251 virtual std::string GetSettingsKey() const = 0;
252 virtual std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const = 0;
253 virtual ComponentFactoryMethod GetStaticConstructor() const = 0;
254 };
255
256
257 class ComponentFactoryBase
258 : public IComponentFactory
259 {
260 private:
261 std::string m_ID;
262 std::string m_SettingsKey;
263 protected:
264 ComponentFactoryBase(const std::string &id, const std::string &settingsKey);
265 void PreConstruct() const;
266 void Initialize(ComponentManager &componentManager, std::shared_ptr<IComponent> component) const;
267 public:
268 virtual ~ComponentFactoryBase();
269 std::string GetID() const override;
270 std::string GetSettingsKey() const override;
271 std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const override = 0;
272 ComponentFactoryMethod GetStaticConstructor() const override = 0;
273 };
274
275
276 template <typename T>
277 class ComponentFactory
278 : public ComponentFactoryBase
279 {
280 public:
ComponentFactory()281 ComponentFactory()
282 : ComponentFactoryBase(T::g_ID, T::g_SettingsKey)
283 {
284 return;
285 }
286 public:
Construct(ComponentManager & componentManager)287 std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const override
288 {
289 PreConstruct();
290 std::shared_ptr<IComponent> component = std::make_shared<T>();
291 Initialize(componentManager, component);
292 return component;
293 }
StaticConstruct(ComponentManager & componentManager)294 static std::shared_ptr<IComponent> StaticConstruct(ComponentManager &componentManager)
295 {
296 return ComponentFactory().Construct(componentManager);
297 }
GetStaticConstructor()298 virtual ComponentFactoryMethod GetStaticConstructor() const override
299 {
300 return &StaticConstruct;
301 }
302 };
303
304
305 class IComponentManagerSettings
306 {
307 public:
308 virtual bool LoadOnStartup() const = 0;
309 virtual bool KeepLoaded() const = 0;
310 virtual bool IsBlocked(const std::string &key) const = 0;
311 virtual mpt::PathString Path() const = 0;
312 protected:
313 virtual ~IComponentManagerSettings() = default;
314 };
315
316
317 class ComponentManagerSettingsDefault
318 : public IComponentManagerSettings
319 {
320 public:
LoadOnStartup()321 bool LoadOnStartup() const override { return false; }
KeepLoaded()322 bool KeepLoaded() const override { return true; }
IsBlocked(const std::string &)323 bool IsBlocked(const std::string & /*key*/ ) const override { return false; }
Path()324 mpt::PathString Path() const override { return mpt::PathString(); }
325 };
326
327
328 enum ComponentState
329 {
330 ComponentStateUnregistered,
331 ComponentStateBlocked,
332 ComponentStateUnintialized,
333 ComponentStateUnavailable,
334 ComponentStateAvailable,
335 };
336
337
338 struct ComponentInfo
339 {
340 std::string name;
341 ComponentState state;
342 std::string settingsKey;
343 ComponentType type;
344 };
345
346
347 class ComponentManager
348 {
349 friend class ComponentFactoryBase;
350 public:
351 static void Init(const IComponentManagerSettings &settings);
352 static void Release();
353 static std::shared_ptr<ComponentManager> Instance();
354 private:
355 ComponentManager(const IComponentManagerSettings &settings);
356 private:
357 struct RegisteredComponent
358 {
359 std::string settingsKey;
360 ComponentFactoryMethod factoryMethod;
361 std::shared_ptr<IComponent> instance;
362 std::weak_ptr<IComponent> weakInstance;
363 };
364 typedef std::map<std::string, RegisteredComponent> TComponentMap;
365 const IComponentManagerSettings &m_Settings;
366 TComponentMap m_Components;
367 private:
368 bool IsComponentBlocked(const std::string &settingsKey) const;
369 void InitializeComponent(std::shared_ptr<IComponent> component) const;
370 public:
371 void Register(const IComponentFactory &componentFactory);
372 void Startup();
373 std::shared_ptr<const IComponent> GetComponent(const IComponentFactory &componentFactory);
374 std::shared_ptr<const IComponent> ReloadComponent(const IComponentFactory &componentFactory);
375 std::vector<std::string> GetRegisteredComponents() const;
376 ComponentInfo GetComponentInfo(std::string name) const;
377 mpt::PathString GetComponentPath() const;
378 };
379
380
381 struct ComponentListEntry
382 {
383 ComponentListEntry *next;
384 void (*reg)(ComponentManager &componentManager);
385 };
386
387 bool ComponentListPush(ComponentListEntry *entry);
388
389 template <typename TComponent>
390 struct ComponentRegisterer
391 {
RegisterComponentComponentRegisterer392 static inline void RegisterComponent(ComponentManager &componentManager)
393 {
394 componentManager.Register(ComponentFactory<TComponent>());
395 }
GetComponentListEntryComponentRegisterer396 static inline ComponentListEntry &GetComponentListEntry()
397 {
398 static ComponentListEntry s_ComponentListEntry = {nullptr, &RegisterComponent};
399 return s_ComponentListEntry;
400 }
401 static inline bool g_ComponentRegistered = ComponentListPush(&GetComponentListEntry());
402 };
403
404 #define MPT_DECLARE_COMPONENT_MEMBERS(name, settingsKey) \
405 public: \
406 static constexpr const char *g_ID = #name ; \
407 static constexpr const char *g_SettingsKey = settingsKey ; \
408 static inline ComponentRegisterer< name > s_ComponentRegisterer; \
409 /**/
410
411
412 template <typename type>
GetComponent()413 std::shared_ptr<const type> GetComponent()
414 {
415 return std::dynamic_pointer_cast<const type>(ComponentManager::Instance()->GetComponent(ComponentFactory<type>()));
416 }
417
418
419 template <typename type>
ReloadComponent()420 std::shared_ptr<const type> ReloadComponent()
421 {
422 return std::dynamic_pointer_cast<const type>(ComponentManager::Instance()->ReloadComponent(ComponentFactory<type>()));
423 }
424
425
GetComponentPath()426 inline mpt::PathString GetComponentPath()
427 {
428 return ComponentManager::Instance()->GetComponentPath();
429 }
430
431
432 #else // !MPT_COMPONENT_MANAGER
433
434
435 #define MPT_DECLARE_COMPONENT_MEMBERS(name, settingsKey)
436
437
438 template <typename type>
GetComponent()439 std::shared_ptr<const type> GetComponent()
440 {
441 static std::weak_ptr<type> cache;
442 static mpt::mutex m;
443 mpt::lock_guard<mpt::mutex> l(m);
444 std::shared_ptr<type> component = cache.lock();
445 if(!component)
446 {
447 component = std::make_shared<type>();
448 component->Initialize();
449 cache = component;
450 }
451 return component;
452 }
453
454
455 #endif // MPT_COMPONENT_MANAGER
456
457
458 // Simple wrapper around std::shared_ptr<ComponentType> which automatically
459 // gets a reference to the component (or constructs it) on initialization.
460 template <typename T>
461 class ComponentHandle
462 {
463 private:
464 std::shared_ptr<const T> component;
465 public:
ComponentHandle()466 ComponentHandle()
467 : component(GetComponent<T>())
468 {
469 return;
470 }
~ComponentHandle()471 ~ComponentHandle()
472 {
473 return;
474 }
IsAvailable()475 bool IsAvailable() const
476 {
477 return component && component->IsAvailable();
478 }
get()479 const T *get() const
480 {
481 return component.get();
482 }
483 const T &operator*() const
484 {
485 return *component;
486 }
487 const T *operator->() const
488 {
489 return &*component;
490 }
491 #if MPT_COMPONENT_MANAGER
Reload()492 void Reload()
493 {
494 component = nullptr;
495 component = ReloadComponent<T>();
496 }
497 #endif
498 };
499
500
501 template <typename T>
IsComponentAvailable(const ComponentHandle<T> & handle)502 bool IsComponentAvailable(const ComponentHandle<T> &handle)
503 {
504 return handle.IsAvailable();
505 }
506
507
508 OPENMPT_NAMESPACE_END
509