2016-09-09 15 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. Это не очень важно, так как я могу ограничить типы, которые хочу поддерживать, но мне действительно интересно, как templatize список типов без использования сторонней библиотеки. Или есть совершенно другой и лучший метод, который я могу использовать с похожим чистым синтаксисом и безопасностью типов для передачи сообщений?

+0

повышение :: любой полезно здесь –

+0

я не хочу использовать библиотеку третий участник –

+0

взглянуть на код подталкивания :: любой и переопределение его (это может занять некоторое время). или подождите, пока C++ 17, тогда у вас будет std :: any – 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

Я беззастенчиво принял свой ответ :) Используя пакет повышения скорости 140 Мб только для 30 строк кода? Спасибо, но нет! –

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 :: версиями, если у вас есть доступные.

Я использовал вариант, потому что он эффективно обслуживает большинство случаев использования с безопасностью во время компиляции.

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 точно. Зачем изобретать хорошо поддержанное колесо? –