2016-04-16 28 views
14

這是一個問題與答案,其目的是邀請讀者建議自己的解決方案。
我很確定那裏有比我更聰明的方法,所以我想知道這些解決方案是什麼。
請通過添加您自己的答案來分享您的知識!
事件發射器和成員方法的自動註冊爲聽衆


的目標是創建一個發射器類可以用於分發一些事件。

我想要在發射器中的一個重要特性是易於使用的註冊設施將偵聽器附加到發射器。

換句話說,我不想編寫旨在將所有聽衆附加到發射器的函數/方法,因爲它可能容易出錯,我發現自己不止一次尋找一個由於因此錯過了代碼行(當然,該行將註冊第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 */ } 
}; 

我怎麼寫這樣的發射器?

+1

呃.. 。你想要'emitter.reg(s)'實際上做什麼?無論如何,'emitter'是什麼?它是類模板的一個實例嗎?你知道所有的'E1','E2',...類型嗎? – Barry

+0

或多或少。如果我想有一個像Emitter 這樣的定義,我必須知道它們。類模板?也許,這取決於實際的實施,這就是我提出的答案。 – skypjack

+0

我寫了一個小型庫,解決了一個非常類似的問題。 tldr將對訂戶類型使用類型擦除,並通過std :: type_index爲類型擦除的訂戶建立索引。該lib是https://github.com/mmcshane/eventbus。我發佈的評論,而不是一個答案,以避免出現自我推銷。 – mpm

回答

2

這遵循強烈基於模板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(); 
} 
7

首先包括:

#include <iostream> 
#include <vector> 
#include <type_traits> 
#include <utility> 
#include <functional> 

我們使用void_tdetection helper

template<class ...> 
using void_t = void; 

我們定義一個性狀使用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_; 
}; 
3

我的版本不繼承:

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_; 
}; 

Demo

+0

使用元組的想法很好。 – ysdx