Code Review Asked on October 27, 2021
I am making a game event messenger based on the observer pattern and I’d really appreciate some feedback on this please. The intent behind it is that I am sending in a function pointer so that it can work for the broadcasting. Also using the subscribers memory address as a way of detecting it for unsubscribe.
Thank you in advance.
enum class eGameEventType
{
SpawnPickup = 0,
Max = SpawnPickup
};
namespace GameEvents
{
template <eGameEventType T>
struct BaseEvent
{
static eGameEventType getType() { return T; };
};
struct SpawnPickUp : public BaseEvent<eGameEventType::SpawnPickup>
{
SpawnPickUp(int type, const std::string& name)
: type(type),
name(name)
{}
int type;
std::string name;
};
}
struct Listener
{
Listener(const std::function<void(const void*)>& fp, const void* ownerAddress)
: m_listener(fp),
m_ownerAddress(ownerAddress)
{}
Listener(Listener&& orig) noexcept
: m_listener(orig.m_listener),
m_ownerAddress(orig.m_ownerAddress)
{
orig.m_listener = nullptr;
orig.m_ownerAddress = nullptr;
}
Listener& operator=(Listener&& orig) noexcept
{
m_listener = orig.m_listener;
m_ownerAddress = orig.m_ownerAddress;
orig.m_listener = nullptr;
orig.m_ownerAddress = nullptr;
return *this;
}
std::function<void(const void*)> m_listener;
const void* m_ownerAddress;
};
class GameEventMessenger
{
public:
static GameEventMessenger& getInstance()
{
static GameEventMessenger instance;
return instance;
}
template <typename GameEvent>
void subscribe(const std::function<void(const GameEvent&)>& gameEvent, const void* ownerAddress)
{
auto& listeners = m_listeners[static_cast<int>(GameEvent::getType())];
assert(!isOwnerAlreadyRegistered(listeners, GameEvent::getType(), ownerAddress));
listeners.emplace_back(reinterpret_cast<std::function<void(const void*)> const&>(gameEvent), ownerAddress);
}
template <typename GameEvent>
void unsubscribe(const void* ownerAddress)
{
auto& listeners = m_listeners[static_cast<int>(GameEvent::getType())];
assert(isOwnerAlreadyRegistered(listeners, GameEvent::getType(), ownerAddress));
auto iter = std::find_if(listeners.begin(), listeners.end(), [ownerAddress](const auto& listener)
{
return listener.m_ownerAddress == ownerAddress;
});
assert(iter != listeners.end());
listeners.erase(iter);
}
template <typename GameEvent>
void broadcast(GameEvent gameEvent)
{
const auto& listeners = m_listeners[static_cast<int>(GameEvent::getType())];
for (const auto& listener : listeners)
{
reinterpret_cast<std::function<void(const GameEvent&)> const&>(listener.m_listener)(gameEvent);
}
}
private:
GameEventMessenger() {}
std::array<std::vector<Listener>, static_cast<size_t>(eGameEventType::Max) + 1> m_listeners;
bool isOwnerAlreadyRegistered(const std::vector<Listener>& listeners, eGameEventType gameEventType, const void* ownerAddress) const
{
assert(ownerAddress != nullptr);
if (!listeners.empty())
{
auto result = std::find_if(listeners.cbegin(), listeners.cend(), [ownerAddress](const auto& listener)
{
return listener.m_ownerAddress == ownerAddress;
});
return result != listeners.cend();
}
else
{
return false;
}
}
};
Working Example:
GameEventMessenger::getInstance().subscribe<GameEvents::AddToInventory>(std::bind(&Player::onAddToInventory, this, std::placeholders::_1), this);
void Player::onAddToInventory(const GameEvents::AddToInventory & gameEvent)
{
m_inventory.add(gameEvent.type);
}
GameEventMessenger::getInstance().unsubscribe<GameEvents::AddToInventory>(this);
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP