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);
仅此而已,即使将来需要在structs
中添加一个侦听器,例如:
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_;
};