Add named events
This commit is contained in:
12
openVulkanoCpp/Base/Event.cpp
Normal file
12
openVulkanoCpp/Base/Event.cpp
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
openVulkanoCpp/Host/iOS/NamedEventProcessor.h
Normal file
23
openVulkanoCpp/Host/iOS/NamedEventProcessor.h
Normal 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;
|
||||
};
|
||||
}
|
||||
150
openVulkanoCpp/Host/iOS/NamedEventProcessor.mm
Normal file
150
openVulkanoCpp/Host/iOS/NamedEventProcessor.mm
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user