C++ 事件发射器和成员方法作为侦听器的自动注册

C++ 事件发射器和成员方法作为侦听器的自动注册,c++,templates,events,c++14,variadic-templates,C++,Templates,Events,C++14,Variadic Templates,这是一个有答案的问题,目的是邀请读者提出自己的解决方案。 我很确定,有比我更聪明的方法,所以我想知道这些解决方案是什么。 请通过添加您自己的答案来分享您的知识 目标是创建一个发射器类,该类可用于分派一些事件 我想在发射器中使用的一个重要功能是一个易于使用的注册工具,用于将侦听器连接到发射器 换句话说,我不想编写旨在将所有侦听器连接到发射器的函数/方法,因为它可能容易出错,我不止一次地发现自己在寻找一个错误,这是由于缺少一行代码(当然,这行代码会注册第N个侦听器) 想象一下以下结构: struc

这是一个有答案的问题,目的是邀请读者提出自己的解决方案。
我很确定,有比我更聪明的方法,所以我想知道这些解决方案是什么。
请通过添加您自己的答案来分享您的知识


目标是创建一个发射器类,该类可用于分派一些事件

我想在发射器中使用的一个重要功能是一个易于使用的注册工具,用于将侦听器连接到发射器

换句话说,我不想编写旨在将所有侦听器连接到发射器的函数/方法,因为它可能容易出错,我不止一次地发现自己在寻找一个错误,这是由于缺少一行代码(当然,这行代码会注册第N个侦听器)

想象一下以下结构:

struct E1 { };

struct S {
    void receive(const E1 &ev) { /* do something */ }
};
我正在寻找的注册设施就是这样一种解决方案,下面几行代码就足够了:

S s;
emitter.reg(s);
仅此而已,即使将来需要在struct
s
中添加一个侦听器,例如:

struct E1 { };
struct E2 { };

struct S {
    void receive(const E1 &ev) { /* do something */ }
    void receive(const E2 &ev) { /* do something */ }
};

我怎样才能编写这样一个发射器呢?

下面是一个完全基于模板和sfinae的发射器类的例子。
可以使用行
g++-g-std=c++14 main.cpp

我试图将代码简化为一个最小的示例,因此只使用裸指针和几个成员方法

#include <functional>
#include <vector>
#include <cassert>

template<class E>
struct ETag { using type = E; };

template<int N, int M>
struct Choice: public Choice<N+1, M> { };

template<int N>
struct Choice<N, N> { };

template<int S, class... T>
class Base;

template<int S, class E, class... O>
class Base<S, E, O...>: public Base<S, O...> {
    using OBase = Base<S, O...>;

protected:
    using OBase::get;
    using OBase::reg;

    std::vector<std::function<void(const E &)>>& get(ETag<E>) {
        return vec;
    }

    template<class C>
    auto reg(Choice<S-(sizeof...(O)+1), S>, C* ptr)
    -> decltype(std::declval<C>().receive(std::declval<E>())) {
        using M = void(C::*)(const E &);
        M m = &C::receive;
        std::function<void(const E &)> fn = std::bind(m, ptr, std::placeholders::_1);
        vec.emplace_back(fn);
        OBase::reg(Choice<S-sizeof...(O), S>{}, ptr);
    }

private:
    std::vector<std::function<void(const E &)>> vec;
};

template<int S>
class Base<S> {
protected:
    virtual ~Base() { }
    void get();
    void reg(Choice<S, S>, void*) { }
};

template<class... T>
class Emitter: public Base<sizeof...(T), T...> {
    using EBase = Base<sizeof...(T), T...>;

public:
    template<class C>
    void reg(C *ptr) {
        EBase::reg(Choice<0, sizeof...(T)>{}, ptr);
    }

    template<class E, class... A>
    void emit(A&&... args) {
        auto &vec = EBase::get(ETag<E>{});
        E e(std::forward<A>(args)...);
        for(auto &&fn: vec) fn(e);
    }
};

struct E1 { };
struct E2 { };
struct E3 { };

struct S {
    void receive(const E1 &) { e1 = !e1; }
    void reject(const E2 &) { e2 = !e2; }
    void receive(const E3 &) { e3 = !e3; }
    void check() { assert(e1); assert(e2); assert(e3); }
    bool e1{false};
    bool e2{true};
    bool e3{false};
};

int main() {
    S s;
    Emitter<E1, E2, E3> emitter;

    emitter.reg(&s);
    emitter.emit<E1>();
    emitter.emit<E2>();
    emitter.emit<E3>();
    s.check();
} 
#包括
#包括
#包括
模板
结构ETag{using type=E;};
模板
结构选择:公共选择{};
模板
结构选择{};
模板
阶级基础;
模板
类基:公共基{
使用OBase=Base;
受保护的:
使用OBase::get;
使用OBase::reg;
标准::向量和获取(ETag){
返回向量;
}
模板
自动注册(选择,C*ptr)
->decltype(std::declval().receive(std::declval())){
使用M=void(C::*)(const E&);
M=&C::receive;
std::function fn=std::bind(m,ptr,std::占位符::_1);
向量后置(fn);
reg(选择{},ptr);
}
私人:
std::vec;
};
模板
阶级基础{
受保护的:
虚拟~Base(){}
void get();
void reg(选择,void*){}
};
模板
类别:公共基地{
使用EBase=Base;
公众:
模板
无效登记(C*ptr){
EBase::reg(选项{},ptr);
}
模板
无效发射(A&&…参数){
auto&vec=EBase::get(ETag{});
E(标准:正向(参数)…);
用于(自动和&fn:vec)fn(e);
}
};
结构E1{};
结构E2{};
结构E3{};
结构{
无效接收(常量E1&{E1=!E1;}
无效拒绝(常量E2&{E2=!E2;}
无效接收(常量E3&{E3=!E3;}
void check(){assert(e1);assert(e2);assert(e3);}
布尔e1{false};
布尔e2{true};
boole3{false};
};
int main(){
S S;
发射极-发射极;
emitter.reg&s;
emit();
emit();
emit();
s、 检查();
} 
第一个功能包括:

#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>
#include <functional>
我们使用
void\t
定义了一个用于检测
receive()
方法的特征:

template<class C, class E, class X = void_t<>>
struct has_event_handler :
      std::false_type {};

template<class C, class E>
struct has_event_handler<C, E, void_t< decltype(
    std::declval<C>().receive(std::declval<const E>())
) >> : std::true_type {};

template<class C, class E>
constexpr bool has_event_handler_v = has_event_handler<C, E>::value;
注意:我使用了C++17中的
\u v
\u t
变体以获得较短的代码,但为了与C++11兼容,您可能需要使用
结构版本(
typename std::enable_if::type
std::is_base_of::value
,等等)

更新:对于
发射器的递归情况,最好使用组合而不是继承:

template<class E, class... F>
class Emitter<E, F...> {
public:
  // Register:
  template<class C>
  std::enable_if_t<!has_event_handler_v<C,E>> reg(C& callback) {
    Emitter<F...>::reg(callback);
  };
  template<class C>
  std::enable_if_t<has_event_handler<C,E>::value> reg(C& callback) {
    handlers_.push_back([&callback](E const& event) { return callback.receive(event); });
    emitter_.reg(callback);
  };
  void trigger(E const& event)
  {
    for (auto const& handler : handlers_)
      handler(event);    
  }
  template<class G>
  void trigger(G const& event)
  {
    emitter_.trigger(event);
  }
private:
  std::vector<std::function<void(const E&)>> handlers_;
  Emitter<F...> emitter_;
};
模板
类发射器{
公众:
//登记册:
模板
std::启用\u if\u t>reg(C&callback){
发射器::reg(回调);
};
模板
std::启用_if_t reg(C&callback){
处理程序u.push_back([&callback](E const&event){return callback.receive(event);});
发射器注册(回调);
};
无效触发器(E常量和事件)
{
for(自动常量和处理程序:处理程序)
处理者(事件);
}
模板
无效触发器(G常量和事件)
{
发射器触发(事件);
}
私人:
向量处理器;
发射极发射极;
};

我的无继承版本:

template <typename C, typename E> std::false_type has_event_handler_impl(...);
template <typename C, typename E>
auto has_event_handler_impl(int)
-> decltype(static_cast<void>(std::declval<C>().receive(std::declval<const E>())),
            std::true_type{});

template <typename C, typename E>
using has_event_handler = decltype(has_event_handler_impl<C, E>(0));

template <class... Es>
class Emitter {
public:

    template<class C>
    void reg(C& callback) {
        const int dummy[] = { 0, (regT<Es>(callback), 0)...};
        static_cast<void>(dummy); // Avoid unused variable warning
    }

    template <typename E>
    void emit(const E& event)
    {
        for (auto const& handler : get_vector<E>()) {
            handler(event);
        }
    }

private:
    template <typename E, typename C>
    std::enable_if_t<has_event_handler<C, E>::value>
    regT(C& callback)
    {
        auto lambda = [&callback](const E& event) { return callback.receive(event); };
        get_vector<E>().push_back(lambda);
    }

    template <typename E, typename C>
    std::enable_if_t<!has_event_handler<C, E>::value>
    regT(C&)
    {
        /* Empty */
    }

    template <typename E>
    std::vector<std::function<void(const E&)>>& get_vector()
    {
        return std::get<std::vector<std::function<void(const E&)>>>(handlers_);
    }

private:
    std::tuple<std::vector<std::function<void(const Es&)>>...> handlers_;
};
template std::false\u类型具有\u事件\u处理程序\u impl(…);
模板
自动具有\u事件\u处理程序\u impl(int)
->decltype(静态_转换(std::declval().receive(std::declval()),
std::真_类型{});
模板
使用has_event_handler=decltype(has_event_handler_impl(0));
模板
类发射器{
公众:
模板
作废登记(C&callback){
常量int dummy[]={0,(regT(回调),0);
static_cast(dummy);//避免未使用的变量警告
}
模板
无效发射(常数E和事件)
{
for(自动常量和处理程序:get_vector()){
处理者(事件);
}
}
私人:
模板
std::如果启用,则启用
注册主任(C&C)
{
自动lambda=[&callback](const E&event){return callback.receive(event);};
获取向量();
}
模板
std::如果值>
注册(C&)
{
/*空的*/
}
模板
std::vector和get_vector()
{
返回std::get(处理程序);
}
私人:
std::元组处理程序;
};

呃。。。您希望发射器.reg
实际执行什么操作?那么什么是发射器呢?它是类模板的实例吗?你知道所有的
E1
E2
。。。提前输入?或多或少。如果我想有一个像
Emitter
这样的定义,我必须知道它们。类模板?也许,这取决于实际的实现,这就是我提出的答案。我写了一个小型的库来解决一个非常类似的问题。tldr将对订阅服务器类型使用类型擦除,并通过std::type_index对已擦除的订阅服务器类型进行索引。自由党是。我是作为一个评论而不是一个答案发布的
// Events
struct E1 {};
struct E2 {};
struct E3 {};

// Handler
struct handler {
  void receive(const E1&)
  {
    std::cerr << "E1\n";
  }
  void receive(const E2&)
  {
    std::cerr << "E2\n";
  }
};

// Check the trait:
static_assert(has_event_handler_v<handler, E1>, "E1");
static_assert(has_event_handler_v<handler, E2>, "E2");
static_assert(!has_event_handler_v<handler, E3>, "E3");

int main()
{
  Emitter<E1, E2> emitter;
  handler h;
  emitter.reg(h);
  emitter.trigger(E1());
  emitter.trigger(E2());
}
template<class E, class... F>
class Emitter<E, F...> {
public:
  // Register:
  template<class C>
  std::enable_if_t<!has_event_handler_v<C,E>> reg(C& callback) {
    Emitter<F...>::reg(callback);
  };
  template<class C>
  std::enable_if_t<has_event_handler<C,E>::value> reg(C& callback) {
    handlers_.push_back([&callback](E const& event) { return callback.receive(event); });
    emitter_.reg(callback);
  };
  void trigger(E const& event)
  {
    for (auto const& handler : handlers_)
      handler(event);    
  }
  template<class G>
  void trigger(G const& event)
  {
    emitter_.trigger(event);
  }
private:
  std::vector<std::function<void(const E&)>> handlers_;
  Emitter<F...> emitter_;
};
template <typename C, typename E> std::false_type has_event_handler_impl(...);
template <typename C, typename E>
auto has_event_handler_impl(int)
-> decltype(static_cast<void>(std::declval<C>().receive(std::declval<const E>())),
            std::true_type{});

template <typename C, typename E>
using has_event_handler = decltype(has_event_handler_impl<C, E>(0));

template <class... Es>
class Emitter {
public:

    template<class C>
    void reg(C& callback) {
        const int dummy[] = { 0, (regT<Es>(callback), 0)...};
        static_cast<void>(dummy); // Avoid unused variable warning
    }

    template <typename E>
    void emit(const E& event)
    {
        for (auto const& handler : get_vector<E>()) {
            handler(event);
        }
    }

private:
    template <typename E, typename C>
    std::enable_if_t<has_event_handler<C, E>::value>
    regT(C& callback)
    {
        auto lambda = [&callback](const E& event) { return callback.receive(event); };
        get_vector<E>().push_back(lambda);
    }

    template <typename E, typename C>
    std::enable_if_t<!has_event_handler<C, E>::value>
    regT(C&)
    {
        /* Empty */
    }

    template <typename E>
    std::vector<std::function<void(const E&)>>& get_vector()
    {
        return std::get<std::vector<std::function<void(const E&)>>>(handlers_);
    }

private:
    std::tuple<std::vector<std::function<void(const Es&)>>...> handlers_;
};