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