1

私が取り組んでいるアプリケーションは、コードが 16 ビット プロセッサを対象として生成された埋め込みシステムから C スタイルの構造体を受け取ります。組み込みシステムと対話するアプリケーションは、32 ビット gcc コンパイラまたは 32 ビット MSVC c++ コンパイラで構築されています。アプリケーションと組み込みシステム間の通信は、イーサネットまたはモデムを介した UDP パケットを介して行われます。

UDP パケット内のペイロードは、さまざまな C スタイルの構造体で構成されています。アプリケーション側では、C++ スタイルreinterpret_castは符号なしバイト配列を取得し、それを適切な構造体にキャストできます。

ただし、reinterpret_cast構造体に列挙値が含まれていると問題が発生します。16 ビットの Watcom コンパイラは、列挙値を uint8_t 型として扱います。ただし、アプリケーション側では、列挙値は 32 ビット値として扱われます。列挙値を含むパケットを受信すると、アプリケーション側の構造体のサイズが組み込み側の構造体よりも大きいため、データが文字化けします。

これまでのところ、この問題の解決策は、アプリケーション側の構造体内の列挙型を uint8_t に変更することでした。ただし、メンバーを列挙型として使用できなくなったため、これは最適なソリューションではありません。

私が探しているのは、アプリケーション側でソースの構造体定義を改ざんすることなく、単純なキャスト操作を使用できるようにするソリューションです。こうすることで、アプリケーションの上位層で構造体をそのまま使用できます。

4

4 に答える 4

2

前述のように、問題に正しく対処するには、適切なシリアライゼーションとデシリアライゼーションが必要です。

しかし、いくつかのハックを試すことができないという意味ではありません。

オプション1:特定のコンパイラが列挙型のパッキングをサポートしている場合(私の場合、Windowsのgcc 4.7)、これはうまくいくかもしれません:

typedef enum { VALUE_1 = 1, VALUE_2, VALUE_3 }__attribute__ ((__packed__)) TheRealEnum;

オプション 2:

特定のコンパイラが 4 バイト未満のクラス サイズをサポートしている場合は、変換に演算子のオーバーロードを使用するHackedEnumクラスを使用できます (gcc属性が不要な場合があることに注意してください)。

class HackedEnum
{
private:
    uint8_t evalue;
public:
    void operator=(const TheRealEnum v) { evalue = v; };
    operator TheRealEnum() { return (TheRealEnum)evalue; };
}__attribute__((packed));

構造内の TheRealEnum をHackedEnum置き換えますが、引き続き TheRealEnum として使用します。

それが機能することを確認するための完全な例:

#include <iostream>
#include <stddef.h>

using namespace std;

#pragma pack(push, 1)

typedef enum { VALUE_1 = 1, VALUE_2, VALUE_3 } TheRealEnum;

typedef struct
{
    uint16_t v1;
    uint8_t enumValue;
    uint16_t v2;
}__attribute__((packed)) ShortStruct;

typedef struct
{
    uint16_t v1;
    TheRealEnum enumValue;
    uint16_t v2;
}__attribute__((packed)) LongStruct;

class HackedEnum
{
private:
    uint8_t evalue;
public:
    void operator=(const TheRealEnum v) { evalue = v; };
    operator TheRealEnum() { return (TheRealEnum)evalue; };
}__attribute__((packed));

typedef struct
{
    uint16_t v1;
    HackedEnum enumValue;
    uint16_t v2;
}__attribute__((packed)) HackedStruct;

#pragma pop()

int main(int argc, char **argv)
{
    cout << "Sizes: " << endl
         << "TheRealEnum: " << sizeof(TheRealEnum) << endl
         << "ShortStruct: " << sizeof(ShortStruct) << endl
         << "LongStruct: " << sizeof(LongStruct) << endl
         << "HackedStruct: " << sizeof(HackedStruct) << endl;

    ShortStruct ss;
    cout << "address of ss: " << &ss <<  " size " << sizeof(ss) <<endl
         << "address of ss.v1: " << (void*)&ss.v1 << endl
         << "address of ss.ev: " << (void*)&ss.enumValue << endl
         << "address of ss.v2: " << (void*)&ss.v2 << endl;

    LongStruct ls;
    cout << "address of ls: " << &ls <<  " size " << sizeof(ls) <<endl
         << "address of ls.v1: " << (void*)&ls.v1 << endl
         << "address of ls.ev: " << (void*)&ls.enumValue << endl
         << "address of ls.v2: " << (void*)&ls.v2 << endl;

    HackedStruct hs;
    cout << "address of hs: " << &hs <<  " size " << sizeof(hs) <<endl
         << "address of hs.v1: " << (void*)&hs.v1 << endl
         << "address of hs.ev: " << (void*)&hs.enumValue << endl
         << "address of hs.v2: " << (void*)&hs.v2 << endl;


    uint8_t buffer[512] = {0};

    ShortStruct * short_ptr = (ShortStruct*)buffer;
    LongStruct * long_ptr = (LongStruct*)buffer;
    HackedStruct * hacked_ptr = (HackedStruct*)buffer;

    short_ptr->v1 = 1;
    short_ptr->enumValue = VALUE_2;
    short_ptr->v2 = 3;

    cout << "Values of short: " << endl
            << "v1 = " << short_ptr->v1 << endl
            << "ev = " << (int)short_ptr->enumValue << endl
            << "v2 = " << short_ptr->v2 << endl;

    cout << "Values of long: " << endl
            << "v1 = " << long_ptr->v1 << endl
            << "ev = " << long_ptr->enumValue << endl
            << "v2 = " << long_ptr->v2 << endl;

    cout << "Values of hacked: " << endl
            << "v1 = " << hacked_ptr->v1 << endl
            << "ev = " << hacked_ptr->enumValue << endl
            << "v2 = " << hacked_ptr->v2 << endl;



    HackedStruct hs1, hs2;

    // hs1.enumValue = 1; // error, the value is not the wanted enum

    hs1.enumValue = VALUE_1;
    int a = hs1.enumValue;
    TheRealEnum b = hs1.enumValue;
    hs2.enumValue = hs1.enumValue;

    return 0;
}

私の特定のシステムでの出力は次のとおりです。

Sizes:
TheRealEnum: 4
ShortStruct: 5
LongStruct: 8
HackedStruct: 5
address of ss: 0x22ff17 size 5
address of ss.v1: 0x22ff17
address of ss.ev: 0x22ff19
address of ss.v2: 0x22ff1a
address of ls: 0x22ff0f size 8
address of ls.v1: 0x22ff0f
address of ls.ev: 0x22ff11
address of ls.v2: 0x22ff15
address of hs: 0x22ff0a size 5
address of hs.v1: 0x22ff0a
address of hs.ev: 0x22ff0c
address of hs.v2: 0x22ff0d
Values of short:
v1 = 1
ev = 2
v2 = 3
Values of long:
v1 = 1
ev = 770
v2 = 0
Values of hacked:
v1 = 1
ev = 2
v2 = 3
于 2012-10-27T06:01:29.387 に答える
1

アプリケーション側では、C++ スタイルの reinterpret_cast を使用して、符号なしバイト配列を取得し、適切な構造体にキャストできます。

構造体のレイアウトは、異なる実装間で同じである必要はありません。このように reinterpret_cast を使用することは適切ではありません。

16 ビットの Watcom コンパイラは、列挙値を uint8_t 型として扱います。ただし、アプリケーション側では、列挙値は 32 ビット値として扱われます。

列挙型の基になる型は実装によって選択され、実装定義の方法で選択されます。

これは、reinterpret_cast で問題を引き起こす可能性のある実装間の多くの潜在的な違いの 1 つにすぎません。注意しないと、受信したバッファー内のデータが型に対して適切に整列されない (たとえば、4 バイトの整列が必要な整数が 1 バイトずれてしまう) 実際の整列の問題もあります。パフォーマンス。パディングはプラットフォーム間で異なる場合があり、基本型のサイズは異なる場合があり、エンディアンは異なる場合があります。

私が探しているのは、アプリケーション側でソースの構造体定義を改ざんすることなく、単純なキャスト操作を使用できるようにするソリューションです。こうすることで、アプリケーションの上位層で構造体をそのまま使用できます。

C++11 では、基になる型を指定できる新しい列挙型構文が導入されています。または、手動で宣言された値を持つ事前定義された定数の束とともに、列挙型を整数型に置き換えることができます。これは、あなたが尋ねている問題のみを修正し、他の問題は解決しません。

本当にすべきことは、適切なシリアライズとデシリアライズです。

于 2012-10-27T05:24:19.150 に答える
0

「単純なキャスト操作」とは、必然的にゼロコピーするものではなく、ソースコードで表現されたものを意味する場合、構造体の 2 つのバージョンを書くことができます。要素ごとにコピーして再パックします。その後、コードの残りの部分で通常の型キャストを使用できます。データサイズは根本的に異なるため(別の回答で言及されているC++ 11機能を使用しない限り)、コピーして再パックせずにこれを行うことはできません。

ただし、アプリケーション側で構造体定義に多少の変更を加えても構わない場合は、生の uint8_t 値を処理する必要のないオプションがいくつかあります。uint8_t のサイズであり (コンパイラで可能であると仮定して)、列挙型との間で暗黙的に変換されるクラスの aaronps の回答を使用できます。別の方法として、値を uint8_ts として格納し、構造体で uint8_t データを取得してそれを返す前に列挙型に変換する列挙型値のアクセサー メソッドを作成することもできます。

于 2012-10-27T06:22:49.403 に答える
0

列挙型を 32 ビット数値の共用体の中に入れます。

union
{
  Enumerated val;
  uint32_t valAsUint32;
};

これにより、組み込み側が 32 ビットに拡張されます。両方のプラットフォームがリトルエンディアンで、構造体が最初にゼロで埋められている限り、機能するはずです。ただし、これによりワイヤ形式が変更されます。

于 2012-10-27T04:53:42.827 に答える