2013-11-26 28 views
2

我想構建一個通用推式組件。C++ 11:泛型類型通過std :: function推送<T(const U&)>

我有一個類store<T>其中

  • 具有T噸;成員
  • 有一個void push(const T & t)方法由數據提供者調用。

當推由供應商叫,我想與類型T2的是結果值通知 由std::function< T2(const T&)>和所有客戶端(store<T2>)計算的值。商店客戶首先通過linker<T, T2>對象訂閱商店。

template <class data> class store 
{ 
    data data_; 
    std::list< action<data > > m_links; 
public: 
    void push(const data & e) 
    { 
     data_ = e; 
     for(action<data> a : m_links) 
      a(data_); 
    } 

    void subscribe(action<data> d) 
    { 
     m_links.push_back(d); 
    } 
}; 

鏈接對象:

template < class data1, class data2 > class linker 
{ 
    // source where come the push calls 
    store<data1> & m_source; 
    // targets to be notified after computation 
    std::list<store<data2> * > m_targets; 
    // computation function 
    std::function< data2(const data1 &)> m_func; 

public: 
    linker(store<data1> & source, std::function< data2(const data1 &)> func) 
    : m_source(source), m_func(func) 
    { 
     m_source.subscribe([this](const data1 & d){this->push(d);}); 
    } 
    // add a client 
    void add_target(store<data2> & target) 
    { 
     m_targets.push_back(&target); 
    } 
    void push(const data1 & e) 
    { 
     //compute result 
     data2 d2 = m_func(e); 
     // notify all 
     for(store<data2> * s2 : m_targets) 
     { 
      s2->push(d2); 
     } 
    } 
}; 

使用案例:

int main() 
{ 
    // function : just increment an int, return as double 
    std::function<double(const int &) > f_inc = [](const int& i){ return i+1;}; 
    // function : display the data 
    std::function<int(const double&) > f_display = [](const double& d){ std::cout << "value=" << d << std::endl ; return 0;}; 

    store<int> source; 
    store<double> target, target2; 

    linker<int, double> l(source, f_inc); 
    l.add_target(target); 
    l.add_target(target2); 


    linker<double, int> display(target, f_display); 

    source.push(1); 

    return 0; 
} 

我希望抑制 '連接' 對象的明確的煩躁。我沒有成功,因爲我不知道如何處理這樣一個事實,即當商店客戶訂閱商店對象時,由於它不知道T2類型,所以該對象不能存儲指向商店的商店!

我想編寫類似的東西:

std::function<double(const int &) > f_inc = [](const int& i){ return i+1;}; 
store<int> source; 
store<double> target; 

source.link_to(target, f_inc); 

,並能夠退訂:

source.unlink(target, f_inc); 

或IDS:

id i = source.link_to(target, f_inc); 
source.unsubscribe(i); 

我使用的代碼塊+ MinGW的4.8.1在Windows XP下。 我猜設計模式存在這個用例...

ps:我不能使用提升。

回答

1

在我看來,通過明確性你的意思是Linker有模板參數的事實。

我會設想是這樣的:

class Broker { 
public: 
    Broker(): _lastId(0) {} 

    // 
    // Notification 
    // 
    template <typename T> 
    void notify(store<T> const& source, T const& event) { 
     auto const it = _sources.find(&source); 

     if (it == _sources.end()) { return; } 

     for (size_t id: it->second) { _targets.find(id)->second->invoke(&event); } 
    } // notify 

    // 
    // Subscription 
    // 
    template <typename T, typename U> 
    size_t subscribe(Store<T> const& source, U&& callback) { 
     _targets[++_lastId] = std::unique_ptr<Action>(new ActionT<T>(callback)); 

     _sources[&source].insert(_lastId); 

     return _lastId; 
    } // subscribe 

    template <typename T, typename U> 
    size_t subscribe(Store<T> const& source, U const& callback) { 
     return this->subscribe(source, U{callback}); 
    } // subscribe 

    void unsubscribe(size_t id) { 
     auto const it = _targets.find(id); 

     if (it == _targets.end()) { return; } 

     void const* source = it->second->_source; 

     auto const it2 = _sources.find(source); 
     assert(it != _sources.end()); 

     it2->second.erase(id); 

     if (it2->second.empty()) { _sources.erase(it2); } 

     _targets.erase(it); 
    } // unsubscribe 

    template <typename T> 
    void unsubscribe(store<T> const& source) { 
     auto const it = _sources.find(&source); 

     if (it == _sources.end()) { return; } 

     for (size_t id: it->second) { _targets.erase(id); } 

     _sources.erase(it); 
    } // unsubscribe 

private: 
    // 
    // Action/ActionT<T> perform Type Erasure (here, we erase T) 
    // 
    struct Action { 
     Action(void const* source): _source(source) {} 

     virtual void invoke(void const*) = 0; 

     void const* _source; 
    }; // struct Action 

    template <typename T> 
    class ActionT: Action { 
    public: 
     ActionT(store<T> const& source, std::function<void(T)> f): 
      Action(&source), 
      _callback(std::move(f)) 
     {} 

     virtual void invoke(void const* event) { 
      _callback(T(*static_cast<T const*>(event)); 
     } 

    private: 
     std::function<void(T)> _callback; 
    }; // class ActionT 

    using Targets = std::map<size_t, std::unique_ptr<Action>>; 
    using Sources = std::map<void const*, std::set<size_t>>; 

    size_t _lastId; 
    Targets _targets; 
    Sources _sources; 
}; // class Broker 

正如你可能會看到,相當複雜的...和最糟糕的呢?這仍然是不安全的。具體來說,有終身的問題:

  • ,如果你註冊的是引用外部世界的回調,則必須將這些引用死
  • 之前,這將是清潔劑時,它們就會消失刪除源(將其刪除防止泄漏)

有解決它某些方面,你可能要考慮signal/slots,幫助沒有它綁到一個特定的對象實現這個邏輯。

+0

「explicitness」意思是我不想要一個明確的'鏈接器'角色對象,但我希望它存儲(至少蒙面)在源'store '對象內。我正在測試你的提議(並感謝你的興趣btw)。 – norisknofun

+0

@norisknofun:我看到了,'經紀人'的想法完全不在那裏:)在這種情況下,我建議查看一下信號/插槽,因爲這些都是關於對象間的「分散」通信網絡。 –