1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-10-31 00:07:39 +02:00
Files
vcmi/include/vcmi/events/SubscriptionRegistry.h
2021-02-20 04:57:50 +03:00

167 lines
3.4 KiB
C++

/*
* SubscriptionRegistry.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
class Environment;
namespace events
{
class EventBus;
class DLL_LINKAGE EventSubscription : public boost::noncopyable
{
public:
virtual ~EventSubscription() = default;
};
template <typename E>
class SubscriptionRegistry : public boost::noncopyable
{
public:
using PreHandler = std::function<void(E &)>;
using ExecHandler = std::function<void(E &)>;
using PostHandler = std::function<void(const E &)>;
using BusTag = const void *;
std::unique_ptr<EventSubscription> subscribeBefore(BusTag tag, PreHandler && handler)
{
boost::unique_lock<boost::shared_mutex> lock(mutex);
auto storage = std::make_shared<PreHandlerStorage>(std::move(handler));
preHandlers[tag].push_back(storage);
return make_unique<PreSubscription>(tag, storage);
}
std::unique_ptr<EventSubscription> subscribeAfter(BusTag tag, PostHandler && handler)
{
boost::unique_lock<boost::shared_mutex> lock(mutex);
auto storage = std::make_shared<PostHandlerStorage>(std::move(handler));
postHandlers[tag].push_back(storage);
return make_unique<PostSubscription>(tag, storage);
}
void executeEvent(const EventBus * bus, E & event, const ExecHandler & execHandler)
{
boost::shared_lock<boost::shared_mutex> lock(mutex);
{
auto it = preHandlers.find(bus);
if(it != std::end(preHandlers))
{
for(auto & h : it->second)
(*h)(event);
}
}
if(event.isEnabled())
{
if(execHandler)
execHandler(event);
auto it = postHandlers.find(bus);
if(it != std::end(postHandlers))
{
for(auto & h : it->second)
(*h)(event);
}
}
}
private:
template <typename T>
class HandlerStorage
{
public:
explicit HandlerStorage(T && handler_)
: handler(handler_)
{
}
STRONG_INLINE
void operator()(E & event)
{
handler(event);
}
private:
T handler;
};
using PreHandlerStorage = HandlerStorage<PreHandler>;
using PostHandlerStorage = HandlerStorage<PostHandler>;
class PreSubscription : public EventSubscription
{
public:
PreSubscription(BusTag tag_, std::shared_ptr<PreHandlerStorage> handler_)
: handler(handler_),
tag(tag_)
{
}
virtual ~PreSubscription()
{
auto registry = E::getRegistry();
registry->unsubscribe(tag, handler, registry->preHandlers);
}
private:
BusTag tag;
std::shared_ptr<PreHandlerStorage> handler;
};
class PostSubscription : public EventSubscription
{
public:
PostSubscription(BusTag tag_, std::shared_ptr<PostHandlerStorage> handler_)
: handler(handler_),
tag(tag_)
{
}
virtual ~PostSubscription()
{
auto registry = E::getRegistry();
registry->unsubscribe(tag, handler, registry->postHandlers);
}
private:
BusTag tag;
std::shared_ptr<PostHandlerStorage> handler;
};
boost::shared_mutex mutex;
std::map<BusTag, std::vector<std::shared_ptr<PreHandlerStorage>>> preHandlers;
std::map<BusTag, std::vector<std::shared_ptr<PostHandlerStorage>>> postHandlers;
template <typename T>
void unsubscribe(BusTag tag, T what, std::map<BusTag, std::vector<T>> & from)
{
boost::unique_lock<boost::shared_mutex> lock(mutex);
auto it = from.find(tag);
if(it != std::end(from))
{
it->second -= what;
if(it->second.empty())
{
from.erase(tag);
}
}
}
};
}