2013-04-16 26 views
1

我正在實現一個設計了消息隊列和消息類的C++中的通信機制。也就是說,抽象父項MessageCommunication,其中存在方法Communication::send(Message&)。類Communication將消息發送到適當的消息隊列message_queue,這由消息的類型決定。 (即,對於Msg1它發送到隊列Queue_Msg1並且Msg2被髮送到Queue_Msg2) 每個消息類型將被創建爲來自Message的派生類。使用元編程實現消息隊列管理

主要是,我感興趣的是自動創建隊列。也就是說,如果我決定添加新的消息類型類newMsg,添加消息隊列Queue_newMsg的過程將不需要在Communication類中更改代碼,例如爲每個消息類型創建代碼隊列。因爲這可以在編譯時完成(在編譯時,所有派生消息類都是已知的,所以消息隊列是必需的),我試圖想到一些元編程解決方案,但沒有管理找到這樣的。

使用一些已知的MPL,例如boost/mpl,我該如何實現上述目標?

回答

1

收拾好你的類型轉換成一個列表:

template<typename... Ts> 
struct type_list {}; 

使用該列表和參數包拆包,以創建隊列的std::array。如果你想排隊,他們將需要在tuple

上面的列表意味着索引和類型之間的雙射。每種類型的實例都會返回索引,您可以使用該索引來拾取隊列(在數組中,很容易 - 在tuple中,需要更多工作)。

index_of traits類,找到一種T的一個type_list<Ts...>指數:

template<typename T, typename list, typename=void> 
struct index_of {}; 

template<typename T, typename T0, typename... Ts> 
struct index_of<T, type_list<T0, Ts...>, 
       typename std::enable_if<std::is_same<T, T0>::value>::type 
       > : std::integral_constant<size_t, 0> 
{}; 

template<typename T, typename T0, typename... Ts> 
struct index_of<T, type_list<T0, Ts...>, 
       typename std::enable_if<!std::is_same<T, T0>::value>::type 
       > : std::integral_constant<size_t, 
            index_of<T, type_list<Ts...>>::value+1> 
{}; 

可能實現CRTP基於「消息幫手」同時實現GetTypeIndex,並確保你的類型是在中央消息列表。

這需要C++ 11,在C++ 03中這是非常困難和更有限的。一個C++ 11編譯器也可以處理100多種類型,而不需要額外的模板元編程(至少在理論上有嚴重的元編程,1000或更多),而C++ 03編譯器即使具有強大的元編程庫也可能受限於10種類型。

請注意,這種方法的一個優點是,您理論上可以完全廢除抽象父類,或者至少使用接口(爲什麼應允許人們發送抽象消息?)。您只能發送實際的具體消息類型。這又需要更多的工作(創建使用CRTP的pack-expanded繼承樹來獲取隊列的位置)。

struct MessageBase { 
    virtual size_t GetTypeIndex() const = 0; 
}; 
template<typename D, typename List> 
struct MessageHelper: public MessageBase { 
    static_assert(std::is_base_of< MessageHelper<D,List>, D >::value, "MessageHelper<D> must be inherited from by D"); 
    D* self() { return static_cast<D*>(this); } 
    D const* self() const { return static_cast<D const*>(this); } 
    virtual size_t GetTypeIndex() const final override { 
    return index_of<D,List>::value; 
    } 
}; 

struct A_Queue { 
    std::deque< std::unique_ptr<MessageBase> > data; 
}; 

template<typename type_list> 
struct MessageQueues; 

template<typename... Ts> 
struct MessageQueues<type_list<Ts...>> { 
    std::array< A_Queue, sizeof...(Ts) > queues; 
    void Enqueue(std::unique_ptr<MessageBase> msg) { 
    size_t index = msg->GetTypeIndex(); 
    queues[ index ].data.push-back(std::move(msg)); 
    } 
}; 

對於嚴重粗略的草案實施。

+0

謝謝!我已經採用了你提到的一些東西,雖然沒有尋求完整的元編程解決方案,但我想到了一個使用模板和運行時的中間解決方案。 – Edgepo1nt

1

與運行元編程不同,您可以在運行時註冊不同的消息類型。註冊表可以創建一個隊列向量並提供唯一標識符以最大限度地降低查找成本,或者如果您不太在意,可以始終使用某個ID的映射到適當的隊列中。

雖然我不會推薦它,但如果你真的想編寫一個複雜的模板解決方案,你可以看看類型列表。你需要的所有構建塊都在Alexandrescu(typelist,如何在它們之外構建層次結構以及一些花哨的模板技巧)的Modern C++ Design

+0

你會如何建議實現運行時註冊表?顯然一個通信類會包含一個隊列向量,但是如何在儘量減少代碼更改的情況下這樣做? – Edgepo1nt

+0

@ Edgepo1nt:在'main'中,您可以將每個消息類型註冊到通信類中。如果您有一些可用於索引的標識符(消息類型?),可以直接用於索引隊列向量。如果這是不可能的,你可以在註冊表中生成一個id(第一個打開的位置)。只要需要發送/接收到該類型的新消息,就可以使用該索引訪問通信類的隊列。 –