2017-01-15 16 views
3

私はメッセージバスで使用しているMessage構造を持っていて、メッセージでデータを送信したいと思います。問題は、データの種類が異なることです。多分1つのメッセージのために私はただ1つのintを送るつもりですが、別のもののために、私はいくつかのint、文字列、多分オブジェクトへのポインタさえ送るでしょう。私このような何か行うことができます:STL複数の値を格納するためのコンテナ?

struct Message { 
    std::map<int, int> intPayload; 
    std::map<int, std::string> strPayload; 
    short id; 
}; 

をするだけでなく、この醜いと不潔で、おそらくスペースの無駄、それは私がポインタのような比較的エキゾチックなデータ型を渡す場合を考慮していません例えば、クラスのインスタンスに変換します。これには何を使用すべきですか?

+1

基底クラスと仮想関数を継承し、ポインタを使用:

はまた、静的な訪問者を使用することができますか? –

+1

私の最初の推測は、あなたの状況で可能な限り多形性です。 – DeiDei

+0

@DeiDei私は少し混乱しています、私の理解から多形性はクラス/構造体の継承と関係していますが、多形性はどのようにここで助けますか? – Accumulator

答えて

1

これを実行する方法は多数あります。ここではC++ 17のstd::variantとの例を示します

std::vector<std::variant<int, std::string>> vec1; 

vec1.emplace_back(1); 
vec1.emplace_back("hello"s); 

doSomethingWithInt(std::get<int>(vec1[0])); 
doSomethingWithString(std::get<std::string>(vec1[1])); 

vec1intまたはstd::stringのどちらかである要素のリストです。

std::vector<std::variant<int, std::string>> vec2; 

// ... 

for(auto&& variant : vec1) { 
    variant.visit([](auto value){ 
     using t = decltype(value); 

     if constexpr (std::is_same_v<t, int>) { 
      std::cout << "value is a int!" << std::endl; 
     } else if constexpr (std::is_same_v<t, std::string>) { 
      std::cout << "value is a string!" << std::endl; 
     } 
    }); 
} 
+0

私は彼のケースでは、メッセージを扱うクラス内のバリアントからどのタイプが出てくるのか言うことはできません。 – skypjack

+0

これはVisual Studio 2015では動作しません(私は ''を含めることはできません)、何か間違っていますか?それはちょうどC++ 17をサポートしていませんか? – Accumulator

+0

それはどのタイプであるかを知る多くの方法があります。最善の方法は、メソッドvisit、get_if、およびgetを使用する方法です。ドキュメントはかなり完成しています。 ['std :: variant']をチェックしてください(http:// en。cppreference.com/w/cpp/utility/variant)cppreferenceのページ –

2

継承やポリモーフィズム使った簡単な例:

struct MessageBase 
{ 
    // The function to send *this* message to the receiver 
    virtual void send(ReceiverClass*) = 0; 
}; 

struct MessageInt : MessageBase 
{ 
    int payload; 

    void send(ReceiverClass* receiver) 
    { 
     // Code to send this message type to the receiver... 
    } 
}; 

struct MessageString : MessageBase 
{ 
    std::string payload; 

    void send(ReceiverClass* receiver) 
    { 
     // Code to send this message type to the receiver... 
    } 
}; 

// ... 

// Vector to store the messages 
std::vector<MessageBase*> messages; 

// Add a couple of messages 
messages.push_back(new MessageInt{123}); 
messages.push_back(new MessageString{"Foobar"}); 

// Send the message to some receiver 
for (auto const* message : messages) 
    message->send(some_reciver_object); 

どれgood bookはあなたに多くの情報を与えることができるはずですが。

2

あなたのソリューションは訪問者パターンに基づいています。最小限、実施例として、
は:

Busための実装は自明であるという事実を脇に置きます
struct Listener; 

struct Message { 
    virtual void accept(Listener &) = 0; 
}; 

struct SimpleMessage: Message { 
    void accept(Listener &) override; 
    int i; 
}; 

struct ComplexMessage: Message { 
    void accept(Listener &) override; 
    int i; 
    char c; 
    double d; 
}; 

struct Listener { 
    void visit(SimpleMessage &) {} 
    void visit(ComplexMessage &) {} 
    void listen(Message &m) { m.accept(*this); } 
}; 

void SimpleMessage::accept(Listener &l) { l.visit(*this); } 
void ComplexMessage::accept(Listener &l) { l.visit(*this); } 

struct Bus { 
    Bus(Listener *l): l{l} {} 
    void publish(Message &m) { l->listen(m); } 
private: 
    Listener *l; 
}; 

int main() { 
    Listener l; 
    Bus b{&l}; 

    SimpleMessage sm; 
    ComplexMessage cm; 

    b.publish(sm); 
    b.publish(cm); 
} 

Listenervisitメンバ関数を仮想できることに注意してください。
このようにして、すべてのリスナーをそのクラスから派生させ、必要なメソッドをオーバーライドすることができます。
Busは、実際の派生型が何であるかにかかわらず、Listenerのセットと一般的なMessageを受け入れます。反対側では、メッセージは、それ自体を適切な派生型に昇格させ、指定されたリスナーへの参照を渡します。

ビジターパターンの背後にあるテクニックは、ダブルディスパッチとも知られています。詳細はこちらをご覧ください。

関連する問題