非常に高いパフォーマンス特性を持たない限り、私は自己記述メッセージフォーマットを使用します。これは通常、一般的な形式(たとえばkey = value)を使用しますが、特定の構造体を使用せず、代わりに既知の属性がメッセージの種類を記述し、そのメッセージ型に固有のロジックを使用してそのメッセージから他の属性を抽出できます。
このタイプのメッセージングでは下位互換性が向上しています。新しい属性を追加したい場合は追加して、古いクライアントは追加できます。固定構造を使用するメッセージングはあまりうまくいかない傾向があります。
EDIT:自己記述メッセージフォーマットの詳細。基本的にここでのアイデアは、フィールドの辞書を定義することです。これは、汎用メッセージに含まれるフィールドの世界です。デフォルトのメッセージにはいくつかの必須フィールドが含まれている必要があります。メッセージに他のフィールドが追加されるのはあなた次第です。シリアライゼーション/デシリアライゼーションは非常に簡単で、追加するすべてのフィールドを持つBLOBを構築し、もう一方の端ですべての属性(マップを想像してください)を持つコンテナを作成します。必須フィールドにはタイプを記述することができます。たとえば、辞書にメッセージタイプを指定することができます。これはすべてのメッセージに設定されます。このフィールドを調べて、このメッセージの処理方法を決定します。処理ロジックに入ると、ロジックがコンテナ(マップ)から必要とする他の属性を抽出して処理するだけです。
このアプローチは、最高の柔軟性を提供し、実際に変更されたフィールドのみを送信するようなことを可能にします。今この状態をどのように保つかはあなた次第ですが、メッセージと処理ロジックの間に1対1のマッピングがあるため、継承も合成も必要ありません。このタイプのシステムのスマートさは、フィールドをシリアル化する方法(フィールドがどの辞書の属性であるかを知るために逆シリアル化する方法)に由来します。このようなフォーマットの例として、FIXプロトコルを見てみましょう - 今私はゲームのためにこれを主張しませんが、アイデアは自己記述メッセージが何であるかを示すべきです。
EDIT2:完全な実装はできませんが、ここではスケッチです。
まず私が値型を定義してみましょう - これはフィールドに存在することができる値の典型的なタイプです:
typedef boost::variant<int32, int64, double, std::string> value_type;
今、私はフィールド
struct field
{
int field_key;
value_type field_value;
};
が今ここに私のメッセージがある説明コンテナ
struct Message
{
field type;
field size;
container<field> fields; // I use a generic "container", you can use whatever you want (map/vector etc. depending on how you want to handle repeating fields etc.)
};
ここでは、というメッセージを作成したいとします...更新は、私に
boost::unique_ptr<Message> getTimeSyncMessage()
{
boost::unique_ptr<Message> msg(new Message);
msg->type = { dict::field_type, TIME_SYNC }; // set the type
// set other default attributes for this message type
return msg;
}
は今、私はより多くの属性を設定する適切なスケルトンを生成するファクトリを使用して、私は例えばサポートされているフィールドの辞書を必要とする場所です
namespace dict
{
static const int field_type = 1; // message type field id
// fields that you want
static const int field_time = 2;
:
}
は、だから今私が言うことができ、
boost::unique_ptr<Message> msg = getTimeSyncMessage();
msg->setField(field_time, some_value);
msg->setField(field_other, some_other_value);
: // etc.
は今、あなたは送信する準備ができている、このメッセージの直列化は、単にコンテナをステップ実行し、ブロブに追加されます。 ASCIIエンコーディングまたはバイナリエンコーディングを使用することができます(要件に応じて、最初は前者から始め、後者は後者に移ります)。ここでは、引数のために
1=1|2=10:00:00.000|3=foo
が、私は、あなたが自分の価値観では起こらない保証できる何か他のものを使用することができますフィールドを区切るために
|
を使用:だからのASCIIエンコードされたバージョンは、上記のようなものである可能性があり。バイナリ形式(これは関係ない)では、各フィールドのサイズをデータに埋め込むことができます。 、その後、記入 - (フィールド
1
あなたはタイプを持ってたら)スケルトンを生成するためのファクトリメソッドを使用し、
直列化復元は、ブロブをステップでしょう(そう例えば|
によってseperatingで)適切に各フィールドを抽出しますコンテナ内のすべての属性。 - あなたが特定の属性を取得したいとき後で、何か行うことができます:私はこれが唯一のスケッチである知っている
msg->getField(field_time); // this will return the variant - and you can use boost::get for the specific type.
を、うまくいけば、それは自己記述形式の背後にある考え方を伝えます。基本的なアイデアが得られたら、実行できる最適化がたくさんありますが、それはまったく別のものです...
動的言語では、リフレクションを使用して、実際にシリアル化する必要があるものを確認します。 C++では、インターフェイス関数を呼び出し、シリアル化されたデータを取得します(派生クラスは、シリアル化する必要のあるデータを知っている必要があります)。違いはなんですか? – Shawnone
OK。インタフェース関数は 'void(*)'を返しますか?それはインターフェイス関数なので、私はそれが必要だと思います。 –
ベクトル、またはstruct {char *、length}になります。形式は "messageCategory(固定長)、messageID(固定長)、シリアライズされたデータ"です。 –
Shawnone