#pragma once
/*
PluginFramework
by John Ryland
Copyright (c) 2023
*/
////////////////////////////////////////////////////////////////////////////////////
// Plugin Manager
#include "Plugin.h"
#include <memory>
#include <string>
#include <cstring>
#include <unordered_map>
#include <atomic>
#include <functional>
#include <vector>
// Forward declare file watcher
namespace filewatch {
enum class Event;
template <typename T>
class FileWatch;
}
using FileWatcher = filewatch::FileWatch<std::string>;
namespace PluginFramework {
// Definition of ToString template
template <typename T>
std::string ToString(T value);
// Specializations for ToString from different types
template <> inline std::string ToString(const char* value) { return std::string(value); }
template <> inline std::string ToString(std::string value) { return value; }
template <> inline std::string ToString(bool value) { return std::to_string(value); }
template <> inline std::string ToString(uint8_t value) { return std::to_string(value); }
template <> inline std::string ToString(int8_t value) { return std::to_string(value); }
template <> inline std::string ToString(uint16_t value) { return std::to_string(value); }
template <> inline std::string ToString(int16_t value) { return std::to_string(value); }
template <> inline std::string ToString(uint32_t value) { return std::to_string(value); }
template <> inline std::string ToString(int32_t value) { return std::to_string(value); }
template <> inline std::string ToString(uint64_t value) { return std::to_string(value); }
template <> inline std::string ToString(int64_t value) { return std::to_string(value); }
template <> inline std::string ToString(float value) { return std::to_string(value); }
template <> inline std::string ToString(double value) { return std::to_string(value); }
// Definition of FromString template
template <typename T>
void FromString(T& value, std::string str);
// Specializations for FromString to different types
template <> inline void FromString(const char*& value, std::string str) { value = strdup(str.c_str()); } // potential leak
template <> inline void FromString(std::string& value, std::string str) { value = str; }
template <> inline void FromString(bool& value, std::string str) { value = std::stoul (str, nullptr, 0); }
template <> inline void FromString(uint8_t& value, std::string str) { value = std::stoul (str, nullptr, 0); }
template <> inline void FromString(int8_t& value, std::string str) { value = std::stol (str, nullptr, 0); }
template <> inline void FromString(uint16_t& value, std::string str) { value = std::stoul (str, nullptr, 0); }
template <> inline void FromString(int16_t& value, std::string str) { value = std::stol (str, nullptr, 0); }
template <> inline void FromString(uint32_t& value, std::string str) { value = std::stoul (str, nullptr, 0); }
template <> inline void FromString(int32_t& value, std::string str) { value = std::stol (str, nullptr, 0); }
template <> inline void FromString(uint64_t& value, std::string str) { value = std::stoull(str, nullptr, 0); }
template <> inline void FromString(int64_t& value, std::string str) { value = std::stoll (str, nullptr, 0); }
template <> inline void FromString(float& value, std::string str) { value = std::stof (str, nullptr); }
template <> inline void FromString(double& value, std::string str) { value = std::stod (str, nullptr); }
class State
{
public:
State() {}
~State() {}
template <typename T>
void AddValue(std::string name, T value)
{
mValues[name] = ToString(value);
}
template <typename T>
bool GetValue(std::string name, T& value) const
{
if (mValues.count(name))
FromString(value, mValues.at(name));
return mValues.count(name) != 0;
}
void Reset()
{
mValues.clear();
}
private:
std::unordered_map<std::string, std::string> mValues;
};
class IExtension
{
public:
IExtension() {}
virtual ~IExtension() {}
static std::string Type() { return "IExtension"; }
virtual bool SupportsInterface(std::string interfaceName) const { return (interfaceName == IExtension::Type()); }
virtual void Initialize() = 0;
virtual void Shutdown() = 0;
virtual void Update() = 0;
// For hot-reloading, need to be able to serialize and deserialize
virtual void SerializeState(State& state) = 0;
virtual void DeserializeState(const State& state) = 0;
};
struct ExtensionWrapper
{
IExtension* mExtensionPtr;
State mSavedState;
};
class PluginManager
{
public:
PluginManager();
~PluginManager();
void SetPluginDirectory(const char* pluginDirectoryName);
void Initialize();
void Shutdown();
void Update();
void LoadPlugins();
void RegisterPlugins();
void Reload();
void UnloadPlugin(Plugin& plugin);
// Call by the plugin to register the functionality it can provide
template <typename T>
void RegisterExtension()
{
RegisterExtension(T::Type(), new T);
}
// Call by the plugin to deregister when the plugin is unloaded
template <typename T>
void DeregisterExtension()
{
DeregisterExtension(T::Type());
}
IExtension* FindExtension(std::string typeName)
{
if (mRegisteredExtensions.count(typeName))
return mRegisteredExtensions.at(typeName).mExtensionPtr;
return nullptr;
}
template <typename T>
T* FindExtension()
{
return (T*)FindExtension(T::Type());
}
private:
void RegisterExtension(std::string extensionName, IExtension* extensionPtr)
{
if (mRegisteredExtensions.count(extensionName))
{
ExtensionWrapper& wrapper = mRegisteredExtensions.at(extensionName);
if (wrapper.mExtensionPtr != nullptr)
{
printf("Unexpected, there is already an extension instance with same type %s\n", extensionName.c_str());
delete extensionPtr;
return;
}
wrapper.mExtensionPtr = extensionPtr;
wrapper.mExtensionPtr->DeserializeState(wrapper.mSavedState);
wrapper.mSavedState.Reset();
}
else
{
mRegisteredExtensions[extensionName] = ExtensionWrapper{ extensionPtr };
}
}
// Call by the plugin to deregister when the plugin is unloaded
void DeregisterExtension(std::string extensionName)
{
if (mRegisteredExtensions.count(extensionName))
{
ExtensionWrapper& wrapper = mRegisteredExtensions.at(extensionName);
if (wrapper.mExtensionPtr == nullptr)
{
printf("Unexpected, there isn't already an extension instance when deregistering %s\n", extensionName.c_str());
return;
}
wrapper.mExtensionPtr->SerializeState(wrapper.mSavedState);
delete wrapper.mExtensionPtr;
wrapper.mExtensionPtr = nullptr;
}
else
{
printf("Unexpectedly deregistering an extension that isn't registered %s\n", extensionName.c_str());
}
}
using FileWatcherPtr = std::unique_ptr<FileWatcher>;
std::unordered_map<std::string, ExtensionWrapper> mRegisteredExtensions;
std::string mPluginDirectoryName;
std::unordered_map<std::string, Plugin> mLoadedPlugins;
FileWatcherPtr mFileWatcher;
std::atomic<bool> mNeedReload;
std::atomic<bool> mPendingAdd;
};
} // PluginFramework namespace