2017-05-08 7 views
2

マルチキャストメッセージを受信し、クラスにデータを解析し、キューを介して別のスレッドに の基本クラスポインタを渡すシステムを更新しています。もう一方のスレッドは、クラスからデータ を読み取り、テーブルに格納します。静的キャストと動的キャストを避ける

私たちが受け取るメッセージは2種類あります。ここでは、クラスのデザインです:

class BaseMsg 
{ 
public: 
    BaseMsg(char tp) : msgType(tp) {} 
    virtual ~BaseMsg() = 0; 

    char msgType; 
} 

class MsgType1 : public BaseMsg 
{ 
public: 
    MsgType1(int a, int b) : BaseMsg('1'), val1(a), val2(b) {} 
    virtual ~MsgType1(); 

    int val1, val2; 
} 

class MsgType2 : public BaseMsg 
{ 
public: 
    MsgType2(double a, double b) : BaseMsg('2'), val1(a), val2(b) {} 
    virtual ~MsgType2(); 

    double val1, val2; 
} 

ここで受け取るコードは次のとおりです。

void reciveMessage(char *datablob, MsgQueue *queue) 
{ 
    BaseMsg *msgReceived; 
    char type = datablob[0]; // message type is the first character in the data, 
           // the rest of the blob varies based on that type 
    if (type == '1') { 
     // code for parsing type 1 into 2 ints, i1, i2 
     msgReceived = new MsgType1(i1, i2); 
    } else { 
     // code for parsing type 2 into 2 doubles, d1, d2 
     msgReceived = new MsgType2(d1, d2); 
    } 
    queue->push(msgReceived); 
} 

ここでは別のスレッドで実行されるプロセスのコードは次のとおりです。

void processMessage(MsgQueue *queue) 
{ 
    BaseMsg *msgToProcess = queue->pop(); 

    if (msgToProcess->msgType == '1') { 
     MsgType1 *mt1 = static_cast<MsgType1 *>(msgToProcess); 
     // do stuff with the message type 1 data 
    } else { 
     MsgType2 *mt2 = static_cast<MsgType2 *>(msgToProcess); 
     // do stuff with the message type 2 data 
    } 
} 

私が知っていることをメッセージタイプのチェックがダウンキャスティングはお粗末ですが、 の通信の制約を受けて、私はよりよい解決策を考え出すことができませんでした。ダイナミックキャスト<(パフォーマンス上の理由から 私はしたくない)を使用しても同じ問題が発生します。まだそれを知る必要があります msgToProcessに変換するクラスのタイプ。

キャスト&キャストを取り除く方法についてのご意見はありますか?私はたくさんの経験を持っています C & C++ですが、OOデザインではそれほどではないので、私は何も知らない方法があるかもしれません。

これは、質問を説明するために非常にすっきりした例です。 何百万ものメッセージを毎秒受信できるので、実際には は50種類以上のメッセージタイプであり、パフォーマンスは非常に重要です。

+1

「do stuff」セクションには、あなたがimpできる方法がありますか基本クラスの仮想関数呼び出しの点でそれを尊重しますか? – RyanP

+0

'downcast'が使用されている場所の数を最小限に抑えることはできますが、避けることはできません。繰り返しの少ない方法については、[私の答えは別のSOの投稿](http://stackoverflow.com/a/43038856/434551)を参照してください。 –

+0

RTTI(キャスト)に頼りたくない場合は、区別の付いた共用体を使用することができますが、引き続き列挙型に対してswitch文が必要です。同様のジョブを実行するstd :: variantもあります。私はパフォーマンスを試していませんが、キャッシュのローカリティの恩恵を受けるような間接指定を保存する必要がないため、非常に高速になると思います。 – AlexG

答えて

2

私はクラスBaseMsgの中で例えばdoStuff()と呼ばれる純粋仮想関数を定義します。
各派生クラスは、そのメソッドを実装しています。あなたのprocessMessage
は、あなたは、単にmsgToProcess->doStuff()

+0

私はその解決策を考えましたが、今やメッセージを含むクラスは、 "do stuff"セクションで行われるすべての作業について知る必要があります。これはデータ操作の混乱です。再パッケージング。目標は、2つの部分を最小限に結びつけることでした。そして、 "do stuff"コードで両方の構造セットについて知っておくだけでした。 –

+0

doStuff(BaseProcessor *)はより良いスケーラビリティを提供します。) – felix

+0

これ。 "ダウンキャストのメッセージタイプのチェックがうまくいかないのは分かっていますが..."多態性に頼るだけでいいのです。 –

3

を呼び出して、私は多型の固有の特性を使用してメンバ関数を使用することを示唆している他の人と同意します。

しかし、メッセージクラスをきれいに保つ必要があることも理解しています。私はVisitorパターンを(つまり、多型に依存している)を使用すると、この場合は良いアイデアかもしれないと思う:

class BaseMsg { 
public: 
    virtual void accept(MsgProcessor& p) = 0; 
}; 

class Msg1 : BaseMsg { 
public: 
    void accept(MsgProcessor& p) { p.process(*this); } 
}; 

class Msg2 : BaseMsg { 
public: 
    void accept(MsgProcessor& p) { p.process(*this); } 
}; 

class MsgProcessor { 
public: 
    void process(Msg1& m); 
    void process(Msg2& m); 
} 

クールなことは、機能の一つは、の任意ののために欠落している場合、これはコンパイルされませんということです派生クラス。

編集:固定スタッフはコメントで指摘

EDIT2:

外部コードベース:

class MsgVisitor; 
class BaseMsg { 
public: 
    virtual void accept(MsgVisitor&) = 0; 
}; 

class Msg1; 
class Msg2; 
class MsgVisitor { 
public: 
    virtual void visit(Msg1&) = 0; 
    virtual void visit(Msg2&) = 0; 
}; 

class Msg1 : public BaseMsg { 
public: 
    void accept(MsgVisitor& v) { v.visit(*this); } 
}; 

class Msg2 : public BaseMsg { 
public: 
    void accept(MsgVisitor& v) { v.visit(*this); } 
}; 

ローカルコードベース:

class MsgProcessor : public MsgVisitor { 
public: 
    void visit(Msg1& m) { ... } // do stuff for Msg1 
    void visit(Msg2& m) { ... } 
}; 

void processMessage(MsgQueue *queue) 
{ 
    BaseMsg *msgToProcess = queue->pop(); 
    msgToProcess->accept(MsgProcessor()); 
    //delete msgToProcess; 
} 
2つのコードベースの外部コードベースで&最小限の変更で
+0

これを行うこと、または上記の提案は、論理階層にない「do stuff」領域で使用されるクラス/構造の大規模な書き換えを必要とします。残念なことに、それは12年以上経過した別のグループによって維持されているすべてのレガシーコードです。私は入ってくるデータを再加工し、それをレガシーコードに結びつけることを任されました。そのコードを書き直したいのですが、今はオプションではありません。 :-( –

+0

@DanielJourあなたのコメントは、質問に答えることさえしようとしません。大量に取り除かれた例で、関連性のない項目を選択するだけです。 –

+0

@FlyboyWilsonこれはコメントであり、答えではありませんでした;フィードバックと批判はこのサイトのコメントの意図された使用の1つです) –

関連する問題