2016-09-09 3 views
0

Ich arbeite an einem Messaging-System auf C++. Ich habe;Generisches Messaging

Mit diesen kann ich generische Nachrichten unterschiedlicher Länge bequem senden/empfangen mit z.

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

Dann bekomme ich die Werte;

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]); 
    ... 
} 

Der Nachteil ist, dass ich alle Typen auflisten muss, die ich in der MessageData-Klasse verwenden muss. Dies ist keine große Sache, da ich die Typen begrenzen kann, die ich unterstützen möchte, aber ich bin wirklich neugierig darauf, wie man die Typenliste ohne Verwendung einer 3rd-Party-Bibliothek templatisieren kann. Oder gibt es eine völlig andere und bessere Methode, die ich mit ähnlicher sauberer Syntax und Typsicherheit verwenden kann, um Nachrichten weiterzugeben?

+0

boost :: any ist hier hilfreich –

+0

Ich möchte nicht einen Dritten Bibliothek –

+0

Blick auf den Code der Verstärkung verwenden :: any und reimplementieren es (dies kann eine Weile dauern). oder warte bis C++ 17 dann hast du std :: any – Hayt

Antwort

0

Ich denke, ich habe eine anständige Lösung für mein Problem entwickelt.

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

Dann senden Sie & empfangen Werte mit;

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

Ich nahm schamlos meine Antwort an :) Verwenden eines 140Mb Boost Pack nur für 30 Zeilen Code? Danke aber nein! –

0

Eine Möglichkeit, Ihren Code generic zu machen ist:

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

Unter der Annahme, dass es mehr Leser, mehrere Schreiber Nachricht Bus basierte auf einer nicht priorisierten Warteschlange, ein einfacher ist glaube ich mit beginnen würde etwas wie das: -

Beachten Sie, dass ich boost :: variant/optional verwendet habe. Diese können leicht durch std :: versions ersetzt werden, wenn diese verfügbar sind.

Ich habe Variante verwendet, weil es effizient für die meisten Anwendungsfälle mit kompilierzeit Sicherheit sorgt.

Die Version std/boost :: any erfordert eine erhebliche (und möglicherweise unwillkommene) Sorgfalt für die Benutzer Ihres Busses.

#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

Sobald ich boost :: variant habe, ist meine Frage irrelevant. –

+0

@AliNaciErdem genau. Warum ein gut gewartetes Rad neu erfinden? –