这是问题及答案,其目的是邀请读者提出自己的解决方案。
我很确定那里有比我更聪明的方法,所以我想知道这些解决方案是什么。
请通过添加自己的答案分享您的知识!!
目标是创建一个发射器类,可用于调度一些事件。
我希望在发射器中使用的一个重要功能是易于使用的注册工具,以将侦听器连接到发射器。
换句话说,我不想编写旨在将所有听众连接到发射器的功能/方法,因为它可能容易出错,而且我发现自己不止一次寻找由于错误的代码行(当然,这行会注册第N 个侦听器)。
想象一下以下结构:
struct E1 { };
struct S {
void receive(const E1 &ev) { /* do something */ }
};
我正在寻找的注册工具就是这样一个解决方案,其中以下代码行就足够了:
S s;
emitter.reg(s);
这就是全部,即使将来它还需要将更多的监听器添加到结构S
中,例如:
struct E1 { };
struct E2 { };
struct S {
void receive(const E1 &ev) { /* do something */ }
void receive(const E2 &ev) { /* do something */ }
};
我怎么能写这样的发射器?
答案 0 :(得分:7)
首先包括:
#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>
#include <functional>
我们使用void_t
detection helper:
template<class ...>
using void_t = void;
我们使用receive()
:
void_t
方法的特征
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;
使用它,我们可以定义发射器类。可变参数是它可以管理的事件类型:
template<class...> class Emitter;
// Recursive case:
template<class E, class... F>
class Emitter<E, F...> : Emitter<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_v<C,E>> reg(C& callback) {
handlers_.push_back([&callback](E const& event) { return callback.receive(event); });
Emitter<F...>::reg(callback);
};
void trigger(E const& event)
{
for (auto const& handler : handlers_)
handler(event);
}
template<class G>
void trigger(G const& event)
{
Emitter<F...>::trigger(event);
}
private:
std::vector<std::function<void(const E&)>> handlers_;
};
// Base case:
template<>
class Emitter<> {
public:
template<class C>
void reg(C& callback) {};
template<class E>
void trigger(E const& event)
{
static_assert(!std::is_same<E,E>::value,
"Does not handle this type of event.");
}
};
对于trigger()
部分,另一种解决方案是使用std::enable_if_t<std::is_base_of_v<E, G>>
。
我们可以将它用于:
// 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());
}
注意:我使用了C ++ 17中的_v
和_t
变体,以便获得更短的代码,但为了与C ++ 11兼容,您可能需要使用struct
版本(typename std::enable_if<foo>::type
,std::is_base_of<B,D>::value
等)。
更新:对于Emitter
的递归案例,最好使用合成而不是继承:
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_;
};
答案 1 :(得分:3)
我没有继承的版本:
function open_all() {
var urls = document.getElementById("list_urls").value;
var urls = urls.split('\n');
var totalno = urls.length;
var s;
for (var i = 0; i < totalno; i++) {
s = urls[i];
if (s) {
if (s.substr(0, 7) != 'http://') {
s = 'http://' + s;
}
window.open(s);
}
}
return false;
}
答案 2 :(得分:2)
下面是一个可能的发射器类的完整示例,该示例强烈基于 templates 和 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();
}