7

バイナリ プロトコル (Javad GRIL プロトコル) のデコーダーを作成しています。これは、次の形式のデータを含む約 100 のメッセージで構成されています。

struct MsgData {
    uint8_t num;
    float x, y, z;
    uint8_t elevation;
    ...
};

フィールドは、ANSI でエンコードされた 2 進数で、ギャップなしで相互に続きます。このようなメッセージを解析する最も簡単な方法は、バイトの入力配列を適切な型にキャストすることです。問題は、ストリーム内のデータがパックされている、つまりアライメントされていないことです。

x86 では、これは を使用して解決できます#pragma pack(1)。ただし、これは一部の他のプラットフォームでは機能しないか、位置合わせされていないデータをさらに処理するためにパフォーマンスのオーバーヘッドが発生します。

もう 1 つの方法は、メッセージの種類ごとに特定の解析関数を作成することですが、前述したように、プロトコルには何百ものメッセージが含まれます。

もう 1 つの方法は、Perlunpack()関数のようなものを使用して、メッセージ形式をどこかに保存することです。たとえば、 を#define MsgDataFormat "CfffC"呼び出すことができますunpack(pMsgBody, MsgDataFormat)。これははるかに短いですが、それでもエラーが発生しやすく、冗長です。さらに、メッセージには配列を含めることができるため、形式がより複雑になる可能性があるため、パーサーは遅く複雑になります。

一般的で効果的な解決策はありますか?この投稿を読んでグーグルで検索しましたが、より良い方法が見つかりませんでした。

おそらくC++には解決策がありますか?

4

5 に答える 5

7

OK、以下は VC10 と GCC 4.5.1 ( ideone.com ) でコンパイルされます。C++1x のこのニーズはすべて であり、古いコンパイラでも<tuple>(として) 利用できるはずです。std::tr1::tuple

メンバーごとにいくつかのコードを入力する必要がありますが、それは非常に最小限のコードです。(最後に私の説明を参照してください。)

#include <iostream>
#include <tuple>

typedef unsigned char uint8_t;
typedef unsigned char byte_t;

struct MsgData {
    uint8_t num;
    float x;
    uint8_t elevation;

    static const std::size_t buffer_size = sizeof(uint8_t)
                                         + sizeof(float) 
                                         + sizeof(uint8_t);

    std::tuple<uint8_t&,float&,uint8_t&> get_tied_tuple()
    {return std::tie(num, x, elevation);}
    std::tuple<const uint8_t&,const float&,const uint8_t&> get_tied_tuple() const
    {return std::tie(num, x, elevation);}
};

// needed only for test output
inline std::ostream& operator<<(std::ostream& os, const MsgData& msgData)
{
    os << '[' << static_cast<int>(msgData.num) << ' ' 
       << msgData.x << ' ' << static_cast<int>(msgData.elevation) << ']';
    return os;
}

namespace detail {

    // overload the following two for types that need special treatment
    template<typename T>
    const byte_t* read_value(const byte_t* bin, T& val)
    {
        val = *reinterpret_cast<const T*>(bin);
        return bin + sizeof(T)/sizeof(byte_t);
    }
    template<typename T>
    byte_t* write_value(byte_t* bin, const T& val)
    {
        *reinterpret_cast<T*>(bin) = val;
        return bin + sizeof(T)/sizeof(byte_t);
    }

    template< typename MsgTuple, unsigned int Size = std::tuple_size<MsgTuple>::value >
    struct msg_serializer;

    template< typename MsgTuple >
    struct msg_serializer<MsgTuple,0> {
        static const byte_t* read(const byte_t* bin, MsgTuple&) {return bin;}
        static byte_t* write(byte_t* bin, const MsgTuple&)      {return bin;}
    };

    template< typename MsgTuple, unsigned int Size >
    struct msg_serializer {
        static const byte_t* read(const byte_t* bin, MsgTuple& msg)
        {
            return read_value( msg_serializer<MsgTuple,Size-1>::read(bin, msg)
                             , std::get<Size-1>(msg) );
        }
        static byte_t* write(byte_t* bin, const MsgTuple& msg)
        {
            return write_value( msg_serializer<MsgTuple,Size-1>::write(bin, msg)
                              , std::get<Size-1>(msg) );
        }
    };

    template< class MsgTuple >
    inline const byte_t* do_read_msg(const byte_t* bin, MsgTuple msg)
    {
        return msg_serializer<MsgTuple>::read(bin, msg);
    }

    template< class MsgTuple >
    inline byte_t* do_write_msg(byte_t* bin, const MsgTuple& msg)
    {
        return msg_serializer<MsgTuple>::write(bin, msg);
    }
}

template< class Msg >
inline const byte_t* read_msg(const byte_t* bin, Msg& msg)
{
    return detail::do_read_msg(bin, msg.get_tied_tuple());
}

template< class Msg >
inline const byte_t* write_msg(byte_t* bin, const Msg& msg)
{
    return detail::do_write_msg(bin, msg.get_tied_tuple());
}

int main()
{
    byte_t buffer[MsgData::buffer_size];

    std::cout << "buffer size is " << MsgData::buffer_size << '\n';

    MsgData msgData;
    std::cout << "initializing data...";
    msgData.num = 42;
    msgData.x = 1.7f;
    msgData.elevation = 17;
    std::cout << "data is now " << msgData << '\n';
    write_msg(buffer, msgData);

    std::cout << "clearing data...";
    msgData = MsgData();
    std::cout << "data is now " << msgData << '\n';

    std::cout << "reading data...";
    read_msg(buffer, msgData);
    std::cout << "data is now " << msgData << '\n';

    return 0;
}

私にとって、これは印刷されます

バッファサイズは6
データを初期化しています...データは現在 [0x2a 1.7 0x11] です
データを消去しています...データは現在 [0x0 0 0x0] です
データの読み取り中...データは現在 [0x2a 1.7 0x11] です

MsgData( 3 つのデータ メンバーのみを含むように型を短くしましたが、これはテスト用です。)

メッセージの種類ごとに、そのbuffer_sizestatic 定数と 2 つのget_tied_tuple()メンバー関数 (1constつは非) を定義する必要がありconst、どちらも同じ方法で実装されます。(もちろん、これらは非メンバーでもかまいませんが、それらが関連付けられているデータ メンバーのリストの近くに保つようにしました。)
一部の型 ( など) では、それらと関数std::stringの特別なオーバーロードを追加する必要があります。 . 残りの機構は、すべてのメッセージ タイプで同じままです。detail::read_value()detail::write_value()

完全な C++1x サポートにより、メンバー関数の明示的な戻り値の型を完全に入力する必要がなくなる可能性がありますが、get_tied_tuple()実際にこれを試したことはありません。

于 2011-01-20T16:39:04.057 に答える
3

バイナリ入力を解析するための私の解決策は、Reader クラスを使用することです。そのため、メッセージ エントリごとに読み取る内容を定義でき、リーダーはオーバーラン、アンダーランなどをチェックできます。

あなたの場合:

msg.num = Reader.getChar();
msg.x = Reader.getFloat();
msg.y = Reader.getFloat();
msg.z = Reader.getFloat();
msg.elevation = Reader.getChar();

依然として多くの作業が必要で、エラーが発生しやすいですが、少なくともエラーのチェックには役立ちます。

于 2011-01-20T16:10:05.087 に答える
1

純粋なC++で(プラグマを使用せずに)すべてのメッセージに対して特定の解析ルーチンを作成することを避けることはできないと思います。

すべてのメッセージが単純なPODのCのような構造である場合、最も簡単な解決策はコードジェネレーターを作成することだと思います。他のC ++のものを使用せずに構造体をヘッダーに配置し、単純なパーサー(perl / python / bashスクリプト)を作成します。いくつかの正規表現を使用するだけで十分です)-または、任意のメッセージで変数名を見つけることができるものを探します。次に、それを使用して、次のように、メッセージを読み取るためのコードを自動的に生成します。

YourStreamType & operator>>( YourStreamType &stream, MsgData &msg ) {
    stream >> msg.num >> msg.x >> msg.y >> msg.z >> msg.elevation;
    return stream;
}

メッセージに含まれる基本的なタイプに特化して、次YourStreamTypeoperator>>ことを行う必要があります。

MsgData msg;
your_stream >> msg;
于 2011-01-21T22:11:41.313 に答える
1

簡単な答えはノーです。メッセージが単純にキャストできない特定のバイナリ形式である場合、パーサーを作成するしかありません。メッセージの説明 (xml や簡単に解析できる形式など) がある場合、その説明から自動的に解析コードを生成してみませんか? キャストほど速くはありませんが、各メッセージを手で書くよりもはるかに高速に生成できます...

于 2011-01-20T16:08:49.513 に答える