5

私は実際のメッセージクラスを持っています。

class Specific1Message {
   //various functions to get different types of data
};

class Specific2Message {
   //various functions to get different types of data
};

私が変更することはできません。

これらのメッセージをエンコードおよびデコードするソフトウェア ツールを書き直しています。実行時にどのメッセージをデコード/エンコードするかを決定します。

特定のメッセージの負荷がテキスト ファイルから取得され、実際のシステムを模倣するために再生されます。メッセージは std::list に一時的に保存されます。新規/削除のライフサイクルをより堅牢にするために、スマート ポインターを使用するように依頼されました。

メッセージに関する私の最初のアイデアは、次のようなことをすることでした:-

class proto_msg : public ref_type {
 public:


}

//ref_type はスマート ポインター クラスです

class Specific1msg : public proto_msg {
   public:


  Specific1Message m_msg;  //instance of specific 1 message - composition
};

しかし、ツールには proto_msg* をパラメーターとして受け取る関数があります。したがって、Specific1Message (たとえば) に到達するには、次のようにするだけだと考えていました。

int SpecificMessageHandler::EncodeMsg(proto_msg* msg, unsigned char* buffer, int size)

しかし、Specific1Message を取得する方法は? msg->GetMsg() - しかし、このメソッドを定義するには? それは何を返しますか?

基本クラスで GetMsg() を定義する必要があります。しかし、戻り値の型は何ですか? それは私が理解できないものですか?あるいは、再考が必要かもしれません。

編集すべての応答に感謝します。とりわけ、複数の派遣について学びました。

結局、私はこのようにすることにしました:-

class realproto {
public:
   const char* getName() const { return "realproto"; }
}; 

class real2ndproto {
public:
   const char* get2Name() const { return "real2ndproto"; }
}; 


template<typename T>
class ProtoWrapper : public ref_type {
public:
   ProtoWrapper(T* real) : m_msg(real) {}
   ~ProtoWrapper() { delete m_msg; }  //cannot have smart ptr on real_proto - so do this way

   T* getMsg() { return m_msg; }

private:
   T* m_msg;
};

次に、このように呼び出します

  ref_ptr<ProtoWrapper <realproto> > msg2 = new ProtoWrapper<realproto>(new realproto);

  realproto* pr1 = msg2->getMsg(); //if need underlying protocol

これにより、最小限のコード変更で void* を削除できるはずです。

4

2 に答える 2

4

私が考えることができる唯一のオプションは、テンプレート+ダブルディスパッチです

class proto_msg : public  ref_type{
   public:
      virtual int call_encode (SpecificMessageHandler*, unsigned char* buffer, int size) = 0;
};

template <class M>
class SpecificMesssageTpl : public  proto_msg
 {
   public:
      int call_encode (SpecificMessageHandler* handler, unsigned char* buffer, int size)
      {
          return handler->EncodeMsgSpecific (m_msg, buffer, size);
      }

private:
  M m_msg;  //instance of specific 1 message - composition
};


class SpecificMessageHandler
{
public:
    int SpecificMessageHandler::EncodeMsg(proto_msg* msg, unsigned char* buffer, int size)
   {
        return msg->call_encoder (this, buffer, size);
   }

   int SpecificMessageHandler::EncodeMsgSpecific(Specific1Message * msg, unsigned char* buffer, int size)
   {
    // encode Specific1Message 
   }

   int SpecificMessageHandler::EncodeMsgSpecific(Specific2Message * msg, unsigned char* buffer, int size)
   {
     // encode Specific2Message 
   }
};
于 2012-11-08T17:25:45.597 に答える
0

これを行うためのクリーンでシンプルな方法はたくさんありません。しかし、少し洗練されていないコードで妥協するつもりなら、いくつかの可能性があります。

proto_msgでは、次のように更新するのはどうでしょうか。

class proto_msg : public ref_type
{
public:
    virtual int message_type() = 0;
}

そして、そのサブクラスは次のように更新されました:

class Specific1msg : public proto_msg
{
public:
    static const int message_id = 1;
    virtual int message_type() { return message_id; }
    Specific1Message m_msg;  //instance of specific 1 message - composition
};

次に、if ステートメントを使用してハンドラーを選択できます。

// proto_msg a_message;

if (a_message.message_type() == Specific1msg::message_id)
{
    Specific1msg specific_message = (Specific1msg)a_message;
    // do something with specific_message.msg
}

実際、C++ は、Run Time Type Information (RTTI) の形式でいくつかの単純なイントロスペクション機能をサポートしています。これは、デフォルトでバイナリにコンパイルされるとは限りません。追加のコンパイラ フラグが必要になる場合があります ( -frttiGNU ランド)。RTTI を使用するdynamic_castと、失敗を適切に処理できるタイプキャスト操作を実行できます。

// proto_msg* a_message

Specific1Message* specific_message = dynamic_cast<Specific1Msg*>(a_message));

if (message != nullptr)
{
    // Do something with specific_message.msg
}

等々。これを行うには他にも多くの方法があり、全体として、私は Aleguna の二重ディスパッチ テンプレート マジックを好むと思いますが、このメカニズムは非常に単純明快で、誰にとっても簡単に理解できるはずです!

テストされていない、コンパイルされていない、E&OE などのコード ;-)

于 2012-11-08T17:45:18.100 に答える