2016-09-09 7 views
0

私はC++のメッセージングシステムを開発中です。私が持っています;汎用メッセージング

class MessageData 
{ 
public: 
    typedef std::vector<std::shared_ptr<MessageData>> MessageList; 

    virtual int getValue(std::shared_ptr<int>) { throw "Not implemented!"; }; 
    virtual float getValue(std::shared_ptr<float>) { throw "Not implemented!"; }; 
    virtual std::string getValue(std::shared_ptr<std::string>) { throw "Not implemented!"; }; 
    ... 
    ... 

    virtual ~MessageData() {}; 
}; 

template <typename T> 
class Message : public MessageData 
{ 
    T val; 
public: 
    static std::shared_ptr<Message<T>> Make(T val) { return std::make_shared<Message<T>>(val); }; 
    static T Get(std::shared_ptr<MessageData> in) { return in->getValue(std::make_shared<T>()); }; 
    Message(T i) { val = i; }; 
    T getValue(std::shared_ptr<T> out) override { return *out = val; } 
    ~Message() {}; 
}; 

これらを使用して、私はさまざまな長さのジェネリックメッセージを便利に送受信することができます。

sendMessage(MessageData::MessageList{ 
       Message<std::string>::Make("paint"), 
       Message<int>::Make(14), 
       Message<float>::Make(129.3f), 
       ... 
      }); 

次に値を取得します。

sendMessage(MessageData::MessageList data) { 
    auto a = Message<std::string>::Get(data[0]); 
    auto b = Message<int>::Get(data[1]); 
    auto c = Message<float>::Get(data[2]); 
    ... 
} 

欠点は、私がMessageDataクラスで使用する必要があるすべてのタイプをリストする必要があることです。私がサポートしたいタイプを制限することができるので、これは大きな問題ではありませんが、サードパーティのライブラリを使用せずにタイプリストをテンプレート化する方法について本当に興味があります。それとも、まったく違った、より良い方法があり、同様のクリーンな構文と型の安全性を使ってメッセージを渡すことができますか?あなたのコードをより汎用的にする

+0

ブースト::いずれかが、私は(これはしばらく時間がかかる場合があります)ブーストのコード::いずれにおいても、サードパーティのライブラリ –

+0

ここに便利です。またはC++ 17まで待ってからstd :: anyを使用します –

+0

外観を使用し、それを再実装したくない – Hayt

答えて

0

私は私の問題にまともな解決策を開発したと思います。

class MessageData { 
public: 
    typedef std::vector<std::shared_ptr<MessageData>> MessageList; 
    virtual ~MessageData() {}; 
}; 

template<typename T> 
class Message : public MessageData { 
    T val; 
public: 
    template<typename U> 
    friend U GetMessage(std::shared_ptr<MessageData> in); 

    Message(T i) { val = i; }; 
}; 

template<typename T> 
T GetMessage(std::shared_ptr<MessageData> in) { 
    std::shared_ptr<Message<T>> tmp = std::dynamic_pointer_cast<Message<T>>(in); 
    if (tmp) { 
     return tmp->val; 
    } 
    throw "Incorrect type!"; 
}; 

template<typename T> 
std::shared_ptr<Message<T>> MakeMessage(T val) 
{ 
    return std::make_shared<Message<T>>(val); 
}; 

次に、値を使用して&を送信します。

sendMessage(MessageData::MessageList{ 
       MakeMessage(std::string("paint")), 
       MakeMessage(14), 
       MakeMessage(129.3f), 
       ... 
      }); 

sendMessage(MessageData::MessageList data) { 
    auto a = GetMessage<std::string>(data[0]); 
    auto b = GetMessage<int>(data[1]); 
    auto c = GetMessage<float>(data[2]); 
    ... 
} 
+0

私は恥知らずに私の答えを受け入れました:)コードの30行のためだけに140Mbのブーストパックを使用して?ありがとうございます! –

0

一つの方法は次のとおりです。

template <typename ... Ts> 
class MessageDataImp; 

template <typename T> 
class MessageDataImp<T> 
{ 
public: 
    virtual ~MessageDataImp() = default; 
    virtual T getValue(std::shared_ptr<T>) { throw "Not implemented!"; }; 
}; 

template <typename T, typename ... Ts> 
class MessageDataImp<T, Ts...> : public MessageDataImp<T>, public MessageDataImp<Ts...> 
{ 
public: 
    using MessageDataImp<T>::getValue; 
    using MessageDataImp<Ts...>::getValue; 
}; 

template <typename ... Ts> 
class MessageDataTs : public MessageDataImp<Ts...> 
{ 
public: 
    typedef std::vector<std::shared_ptr<MessageDataTs<Ts...>>> MessageList; 
}; 

using MessageData = MessageDataTs<int, float, std::string>; 
-1

は、それが非優先キューに基づいて、単純な複数のリーダー、マルチライター・メッセージ・バスだと仮定すると、私は私で始まると思います -

boost :: variant/optionalを使用していることに注意してください。利用可能なものがあれば、これらをstd :: versionに簡単に置き換えることができます。

私はバリアントを使用しています。これは、コンパイル時の安全性を考慮してほとんどのユースケースを効率的に処理するためです。

std/boost ::任意のバージョンでは、バスのユーザーにとって重要な(そしておそらくは歓迎されない)注意が必要です。

#include <iostream> 
#include <string> 
#include <queue> 
#include <thread> 
#include <condition_variable> 
#include <boost/variant.hpp> 
#include <boost/optional.hpp> 

template<class Mutex> auto get_lock(Mutex& m) { return std::unique_lock<Mutex>(m); } 

template<class...Types> 
struct message_bus 
{ 
    using message_type = boost::variant<Types...>; 

    void push(message_type msg) { 
     auto lock = get_lock(mutex_); 
     messages_.push(std::move(msg)); 
     lock.unlock(); 
     activity_.notify_one(); 
    } 

    boost::optional<message_type> wait_pop() 
    { 
     boost::optional<message_type> result; 
     auto lock = get_lock(mutex_); 
     activity_.wait(lock, [this] { return this->stopped_ or not this->messages_.empty(); }); 
     if (not messages_.empty()) 
     { 
      result = std::move(messages_.front()); 
      messages_.pop(); 
     } 
     return result; 
    } 

    void signal_stop() 
    { 
     auto lock = get_lock(mutex_); 
     stopped_ = true; 
     lock.unlock(); 
     activity_.notify_all(); 
    } 


    std::queue<message_type> messages_; 
    std::mutex mutex_; 
    std::condition_variable activity_; 
    bool stopped_ = false; 
}; 

static std::mutex emit_mutex; 

template<class T> 
void emit(const T& t) 
{ 
    auto lock = get_lock(emit_mutex); 
    std::cout << std::this_thread::get_id() << ": " << t << std::endl;; 
} 

int main() 
{ 

    using bus_type = message_bus<std::string, int>; 
    bus_type mb; 

    std::vector<std::thread> threads; 
    for (int i = 0 ; i < 10 ; ++i) 
    { 
     threads.emplace_back([&] 
     { 
      for(;;) 
      { 
       auto message = mb.wait_pop(); 
       if (not message) 
        break; 
       boost::apply_visitor([](auto&& data) { emit(data); }, message.value()); 
      } 
     }); 
    } 

    for (int i = 0 ; i < 1000 ; ++i) 
    { 
     mb.push("string: " + std::to_string(i)); 
     mb.push(i); 
    } 
    mb.signal_stop(); 

    for (auto& t : threads) if (t.joinable()) t.join(); 

} 
+0

私はboost :: variantを持っていれば、私の質問は無関係です。 –

+0

@AliNaciErdem正確に。なぜよく整備された車輪を再発明するのですか? –