Add named events

This commit is contained in:
2023-08-11 01:33:32 +02:00
parent 55866a42a5
commit 52914a393e
4 changed files with 260 additions and 2 deletions

View File

@@ -0,0 +1,12 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "Event.hpp"
namespace openVulkanoCpp
{
std::vector<INamedEventProcessor*> INamedEventProcessor::processors;
}

View File

@@ -10,6 +10,8 @@
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <variant>
#include <vector>
namespace openVulkanoCpp
@@ -21,6 +23,34 @@ namespace openVulkanoCpp
virtual void SetInvalid() = 0;
};
class INamedEventProcessor
{
static std::vector<INamedEventProcessor*> processors;
public:
// Keep this in sync with all implementations! synctag: NamedEventProcessor_Parameters_SYNC_TAG
typedef std::variant<std::string, bool, int, float, double> Parameters;
static bool HasProcessor() { return !processors.empty(); }
static void NotifyAll(const std::string& eventName, const std::vector<Parameters>& parameters)
{
for(const auto& processor : processors)
{
processor->Notify(eventName, parameters);
}
}
INamedEventProcessor() { processors.push_back(this); }
virtual ~INamedEventProcessor()
{
processors.erase(std::remove_if(processors.begin(), processors.end(), [this](auto v) { return v==this; }), processors.end());
}
virtual void Notify(const std::string& eventName, const std::vector<Parameters>& parameters) const = 0;
};
template<typename... Arguments>
class Event final
{
@@ -29,7 +59,7 @@ namespace openVulkanoCpp
private:
enum class EventHandlerType { STATIC, INSTANCED, INSTANCED_SHARED_PTR, INSTANCED_WEAK_PTR, FUNCTIONAL };
enum class EventHandlerType { STATIC, INSTANCED, INSTANCED_SHARED_PTR, INSTANCED_WEAK_PTR, FUNCTIONAL, NAMED };
//region Event Handlers
class EventHandler : public IEventHandler
@@ -184,6 +214,33 @@ namespace openVulkanoCpp
private:
Function function;
};
class NamedEventHandler final : public EventHandler
{
std::string m_name;
public:
NamedEventHandler(const std::string& name) :
EventHandler(EventHandlerType::NAMED), m_name(name)
{}
void Notify(Arguments... args) const override
{
if (INamedEventProcessor::HasProcessor())
{
std::vector<INamedEventProcessor::Parameters> attributes;
attributes.reserve(sizeof...(Arguments));
(attributes.emplace_back(args), ...);
INamedEventProcessor::NotifyAll(m_name, attributes);
}
}
protected:
bool ShouldDelete(EventHandler* other) const override
{
return false;
}
};
//endregion
std::vector<std::unique_ptr<EventHandler>> handlers;
@@ -215,6 +272,17 @@ namespace openVulkanoCpp
}
public:
Event() = default;
Event(const std::string& name)
{
RegisterName(name);
}
Event(const Event& event) = default;
Event(Event&& event) = default;
[[nodiscard]] bool HasHandlers() const
{
return !handlers.empty();
@@ -235,6 +303,11 @@ namespace openVulkanoCpp
NotifyAll(std::forward<Arguments>(args)...);
}
IEventHandler* RegisterName(const std::string& name)
{
return Add(new NamedEventHandler(name));
}
template<class Instance, class Method>
IEventHandler* RegisterHandler(Instance* instance, Method method)
{
@@ -391,4 +464,4 @@ namespace openVulkanoCpp
{
return std::pair<std::weak_ptr<Instance>, Method>(instance, method);
}
}
}

View File

@@ -0,0 +1,23 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "Base/Event.hpp"
namespace openVulkanoCpp
{
class NamedEventProcessor final : public INamedEventProcessor
{
std::vector<std::string> m_classNamePrefixes;
public:
NamedEventProcessor() { RegisterClassNamePrefix(""); }
void RegisterClassNamePrefix(const std::string& prefix) { m_classNamePrefixes.push_back(prefix); }
void Notify(const std::string& eventName, const std::vector<Parameters>& parameters) const override;
};
}

View File

@@ -0,0 +1,150 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "NamedEventProcessor.h"
#include "Base/Logger.hpp"
#include <map>
#include <typeindex>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
namespace openVulkanoCpp
{
namespace
{
NamedEventProcessor appleNotificationNamedEventProcessor;
constexpr int LONGEST_TYPE_NAME = std::max({ sizeof(@encode(NSString*)), sizeof(@encode(BOOL)), sizeof(@encode(int)), sizeof(@encode(float)), sizeof(@encode(double)) });
std::map<std::type_index, std::string> TYPE_NAME_MAP = {
{ std::type_index(typeid(std::string)), @encode(NSString*) },
{ std::type_index(typeid(bool)), @encode(BOOL) },
{ std::type_index(typeid(int)), @encode(int) },
{ std::type_index(typeid(float)), @encode(float) },
{ std::type_index(typeid(double)), @encode(double) }
};
// Keep this in sync with INamedEventProcessor! synctag: NamedEventProcessor_Parameters_SYNC_TAG
union ParameterValue
{
NSString* string;
BOOL boolean;
int integer;
float fp32;
double fp64;
};
std::string BuildTypeName(const std::vector<INamedEventProcessor::Parameters>& parameters)
{
std::string typeName("@:");
typeName.reserve(2 + parameters.size() * (LONGEST_TYPE_NAME + 2));
for(const auto& parameter : parameters)
{
typeName.append(std::visit([](auto&& arg) {
return TYPE_NAME_MAP.find(typeid(std::decay_t<decltype(arg)>))->second;
}, parameter));
}
return typeName;
}
template <typename T>
ParameterValue CreateValue(const T& parameter)
{
ParameterValue value;
reinterpret_cast<T&>(value) = parameter;
return value;
}
ParameterValue CreateValue(const std::string& parameter)
{
ParameterValue value;
value.string = [NSString stringWithUTF8String:parameter.c_str()];
return value;
}
std::string SelectorType(Method m)
{
std::string sig;
for (auto i = 0u, count = method_getNumberOfArguments(m); i < count; ++i)
{
auto argType = method_copyArgumentType(m, i);
sig += std::string(argType);
free(argType);
}
return sig;
}
SEL GetInitForClass(Class c, const std::string &initType)
{
SEL init = nullptr;
for (auto clazz = c; !init && clazz; clazz = class_getSuperclass(clazz))
{
if (Method *methods = class_copyMethodList(clazz, nullptr))
{
for (int i = 0; methods[i]; i++)
{
const std::string name = sel_getName(method_getName(methods[i]));
const std::string type = SelectorType(methods[i]);
if (0 == name.find("init") && type == initType)
{
init = method_getName(methods[i]);
break;
}
}
free(methods);
}
}
return init;
}
Class FindGenericEventClass(const std::vector<std::string>& prefixes, const std::string& eventName)
{
for (const std::string& prefix : prefixes)
{
// look for class in all registered class prefixes
NSString* eventClassName = [NSString stringWithUTF8String:(prefix + eventName).c_str()];
if (Class eventClass = NSClassFromString(eventClassName))
{
return eventClass;
}
}
return nullptr;
}
}
void NamedEventProcessor::Notify(const std::string& eventName, const std::vector<Parameters>& parameters) const
{
if (auto eventClass = FindGenericEventClass(m_classNamePrefixes, eventName))
{
if (auto init = GetInitForClass(eventClass, BuildTypeName(parameters)))
{
auto event = [eventClass alloc];
auto inv = [NSInvocation invocationWithMethodSignature:[event methodSignatureForSelector:init]];
[inv setTarget:event];
[inv setSelector:init];
for (int i = 0; i < parameters.size(); i++)
{
auto arg = std::visit([](auto&& arg) { return CreateValue(arg); }, parameters[i]);
[inv setArgument:&arg atIndex:(i + 2)];
}
[inv invoke];
for (Class clazz = eventClass; clazz && clazz != NSObject.class; clazz = class_getSuperclass(clazz))
{
[[NSNotificationCenter defaultCenter] postNotificationName:NSStringFromClass(clazz) object:event userInfo:nil];
}
}
else
{
Logger::MANAGER->error("Cannot find viable constructor for event %s", eventName.c_str());
}
}
else
{
Logger::MANAGER->debug("Cannot find implementation for event %s", eventName);
}
}
}