c++完全通用的事件调度程序

发布于 2025-01-19 17:44:24 字数 1456 浏览 2 评论 0原文

我再次尝试更好地解释我将取得的成就。
我想做这样的事情(受到Unity的Unityevent的启发):

在某些类中声明的公共“变量”:

GameEvent<> OnEnemySpawn = GameEvent<>();
GameEvent<string> OnPlayerSpawn = GameEvent<string>(); 
GameEvent<string, float> OnEnemyDie = GameEvent<string, float>();

推荐其他一些类订阅其方法的转介:

...
enemySpawner.OnEnemySpawn.Subscribe(IncreaseEnemyAliveCountByOne);
...
playerSpawner.OnPlayerSpawn.Subscribe(NewPlayerSpawned);
...
enemy.OnEnemyDie.Subscribe(IncreasePlayerScore);
...
// Subscribed methods declaration
void IncreaseEnemyAliceCountByOne() { ... }
void NewPlayerSpawned(string playerName) { ... }
void IncreasePlayerScore(string playerName, float scoreToAdd) { ... }

然后GameEvent类可以通知事件:

...
OnEnemySpawn.Notify();
...
OnPlayerSpawn.Notify(newPlayer.PlayerName);
...
OnEnemyDie.Notify(playerKiller.PlayerName, scoreOnKill);
...

实际上,我实现了创建此类的声明和订阅部分:

templace<class ... T>
class GameEvent
{
private:
    std::vector<std::function<void(T...)>> _subscribers;

public:
    void Subscribe(std::function<void(T...)> newSubscriber)
  {
    _subscribers.push_back(newSubscriber);
  }
}

使我疯狂的事情是如何实现通知方法。我应该如何知道我收到了多少个参数以及他们

void Notify(T...)
{
    for (std::function<void(T...)> subscriber : _subscribers)
  {
    
  }
}

希望现在有哪些类型,这是一个有效的问题,因为我在此背后失去了理智

I try again to explain better again what I would achieve.
I would like make a thing like this (inspired to Unity's UnityEvent):

Public "variables" declared in some classes:

GameEvent<> OnEnemySpawn = GameEvent<>();
GameEvent<string> OnPlayerSpawn = GameEvent<string>(); 
GameEvent<string, float> OnEnemyDie = GameEvent<string, float>();

Referral where some other classes subscribe their methods:

...
enemySpawner.OnEnemySpawn.Subscribe(IncreaseEnemyAliveCountByOne);
...
playerSpawner.OnPlayerSpawn.Subscribe(NewPlayerSpawned);
...
enemy.OnEnemyDie.Subscribe(IncreasePlayerScore);
...
// Subscribed methods declaration
void IncreaseEnemyAliceCountByOne() { ... }
void NewPlayerSpawned(string playerName) { ... }
void IncreasePlayerScore(string playerName, float scoreToAdd) { ... }

And then GameEvent class would be able to notify the event happens:

...
OnEnemySpawn.Notify();
...
OnPlayerSpawn.Notify(newPlayer.PlayerName);
...
OnEnemyDie.Notify(playerKiller.PlayerName, scoreOnKill);
...

Actually, I achieved the declaration and subscription part creating this class:

templace<class ... T>
class GameEvent
{
private:
    std::vector<std::function<void(T...)>> _subscribers;

public:
    void Subscribe(std::function<void(T...)> newSubscriber)
  {
    _subscribers.push_back(newSubscriber);
  }
}

The thing that makes me crazy is how implement the Notify method. How should I know how many parameters I received and which types they have

void Notify(T...)
{
    for (std::function<void(T...)> subscriber : _subscribers)
  {
    
  }
}

I hope now this is a valid question cause I'm losing my mind behind this

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

归属感 2025-01-26 17:44:24

明显的方式有什么问题?

void Notify(T... args)
{
    // note: no need to write the type if it's quite long
    // note: & means the std::function isn't copied
    for (auto const& subscriber : _subscribers)
    {
        subscriber(args...);
    }
}

What is wrong with the obvious way?

void Notify(T... args)
{
    // note: no need to write the type if it's quite long
    // note: & means the std::function isn't copied
    for (auto const& subscriber : _subscribers)
    {
        subscriber(args...);
    }
}
未央 2025-01-26 17:44:24

这非常有用,正是我想要的,我花了很长时间才添加取消订阅功能。
问题在于比较两个 std::function> 的相等性没有明确定义,因此很难知道要删除哪一个。
我最终做的是使用第二个向量将 void* 存储到订阅对象本身,然后删除同一索引处的侦听器对象和侦听器函数。


#pragma once
#include <functional>
#include <vector>

template<class ... T>
class Event
{
    // The callbacks to the event, called when the event is fired
    std::vector<std::function<void(T...)>> _subscribers;
    // The objects subscribing to the event
    // N.B. keep the two vectors in sync
    std::vector<const void*> _subscriberObjects; 

public:
    void Subscribe(std::function<void(T...)> newSubscriber, const void* subscriber)
    {
        _subscribers.push_back(newSubscriber);
        _subscriberObjects.push_back(subscriber);
    }
    
    void Unsubscribe(const void* subscriber)
    {
        for (int i= 0; i < _subscriberObjects.size(); i++)
        {
            if(_subscriberObjects[i] == subscriber)
            {
                _subscribers.erase(_subscribers.begin() + i);
                _subscriberObjects.erase(_subscriberObjects.begin() + i);
            }
        }
    }

    void Invoke(T... args)
    {
        for (auto const& subscriber : _subscribers)
        {
            subscriber(args...);
        }
    }
};

示例用法(在你的 ListenerClass 中的某个地方)

someEveneInvoker.OnBClicked.Subscribe([this] { OnEventInvoked(); }, this);
someEveneInvoker.OnBClicked.Unsubscribe(this);

老实说,我对我在这方面找到的示例和资源如此之少感到惊讶,因为它是非常常见的,所以我很确定这种实现事件的方法有问题,但它确实现在工作所以...

this was very useful, exactly what I was going for, what took me ages to do was then adding the Unsubscribe function.
The problem is that comparing two std::function<void(...T)>> for equality is not well defined so it's hard to know which one to remove.
What I ended up doing is having a second vector that stores a void* to the subscribing object itself, and then removing the listener object and listener function at the same index.


#pragma once
#include <functional>
#include <vector>

template<class ... T>
class Event
{
    // The callbacks to the event, called when the event is fired
    std::vector<std::function<void(T...)>> _subscribers;
    // The objects subscribing to the event
    // N.B. keep the two vectors in sync
    std::vector<const void*> _subscriberObjects; 

public:
    void Subscribe(std::function<void(T...)> newSubscriber, const void* subscriber)
    {
        _subscribers.push_back(newSubscriber);
        _subscriberObjects.push_back(subscriber);
    }
    
    void Unsubscribe(const void* subscriber)
    {
        for (int i= 0; i < _subscriberObjects.size(); i++)
        {
            if(_subscriberObjects[i] == subscriber)
            {
                _subscribers.erase(_subscribers.begin() + i);
                _subscriberObjects.erase(_subscriberObjects.begin() + i);
            }
        }
    }

    void Invoke(T... args)
    {
        for (auto const& subscriber : _subscribers)
        {
            subscriber(args...);
        }
    }
};

Example usage (somewhere in your ListenerClass)

someEveneInvoker.OnBClicked.Subscribe([this] { OnEventInvoked(); }, this);
someEveneInvoker.OnBClicked.Unsubscribe(this);

I'm honestly surprised by how few examples and resources I found on this as it's something that is pretty common, so I'm pretty sure something is wrong with this approach for implementing events, but it does work for now so...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文