code: Add a proper dynamic loader for components

This loader model should have wider compatibility, as it relies on defined C++ behavior instead of undefined preprocessor behavior. We may even be able to implement a simple dependency system that automatically sorts components into the correct order.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2023-05-14 06:50:27 +02:00 committed by Xaymar
parent 65c45c4461
commit 49f763aa44
2 changed files with 93 additions and 0 deletions

View File

@ -76,6 +76,8 @@
#include "warning-disable.hpp"
#include <fstream>
#include <list>
#include <map>
#include <stdexcept>
#include "warning-enable.hpp"
@ -83,11 +85,60 @@ static std::shared_ptr<streamfx::util::threadpool::threadpool> _threadpool;
static std::shared_ptr<streamfx::gfx::opengl> _streamfx_gfx_opengl;
static std::shared_ptr<streamfx::obs::source_tracker> _source_tracker;
namespace streamfx {
typedef std::list<loader_function_t> loader_list_t;
typedef std::map<loader_priority_t, loader_list_t> loader_map_t;
loader_map_t& get_initializers()
{
static loader_map_t initializers;
return initializers;
}
loader_map_t& get_finalizers()
{
static loader_map_t finalizers;
return finalizers;
}
loader::loader(loader_function_t initializer, loader_function_t finalizer, loader_priority_t priority)
{
auto init_kv = get_initializers().find(priority);
if (init_kv != get_initializers().end()) {
init_kv->second.push_back(initializer);
} else {
get_initializers().emplace(priority, loader_list_t{initializer});
}
// Invert the order for finalizers.
auto ipriority = priority ^ static_cast<loader_priority_t>(0xFFFFFFFFFFFFFFFF);
auto fina_kv = get_finalizers().find(ipriority);
if (fina_kv != get_finalizers().end()) {
fina_kv->second.push_back(finalizer);
} else {
get_finalizers().emplace(ipriority, loader_list_t{finalizer});
}
}
} // namespace streamfx
MODULE_EXPORT bool obs_module_load(void)
{
try {
DLOG_INFO("Loading Version %s", STREAMFX_VERSION_STRING);
// Run all initializers.
for (auto kv : streamfx::get_initializers()) {
for (auto init : kv.second) {
try {
init();
} catch (const std::exception& ex) {
DLOG_ERROR("Initializer threw exception: %s", ex.what());
} catch (...) {
DLOG_ERROR("Initializer threw unknown exception.");
}
}
}
// Initialize global configuration.
streamfx::configuration::initialize();
@ -290,6 +341,19 @@ MODULE_EXPORT void obs_module_unload(void)
// Finalize Thread Pool
_threadpool.reset();
// Run all finalizers.
for (auto kv : streamfx::get_finalizers()) {
for (auto init : kv.second) {
try {
init();
} catch (const std::exception& ex) {
DLOG_ERROR("Finalizer threw exception: %s", ex.what());
} catch (...) {
DLOG_ERROR("Finalizer threw unknown exception.");
}
}
}
DLOG_INFO("Unloaded Version %s", STREAMFX_VERSION_STRING);
} catch (std::exception const& ex) {
DLOG_ERROR("Unexpected exception in function '%s': %s", __FUNCTION_NAME__, ex.what());

View File

@ -5,7 +5,36 @@
#pragma once
#include "common.hpp"
#include "warning-disable.hpp"
#include <cstdint>
#include <functional>
#include "warning-enable.hpp"
namespace streamfx {
/** Simple but efficient loader structure for ordered initia-/finalize.
*
*/
typedef int32_t loader_priority_t;
typedef std::function<void()> loader_function_t;
enum loader_priority : loader_priority_t {
HIGHEST = INT32_MIN,
HIGHER = INT32_MIN / 4 * 3,
HIGH = INT32_MIN / 4 * 2,
ABOVE = INT32_MIN / 4,
NORMAL = 0,
BELOW = INT32_MAX / 4,
LOW = INT32_MAX / 4 * 2,
LOWER = INT32_MAX / 4 * 3,
LOWEST = INT32_MAX,
};
struct loader {
loader(loader_function_t initializer, loader_function_t finalizer, loader_priority_t priority);
// Usage:
// auto loader = streamfx::loader([]() { ... }, []() { ... }, 0);
};
// Threadpool
std::shared_ptr<streamfx::util::threadpool::threadpool> threadpool();