6

組み込みプログラミングでは、ハードウェアを記述するときに、ハードウェア エンジニアが設計した既知の事前定義された位置に構造体要素を配置する必要があることがよくあります。たとえば、次の単純化された例のように、約 100 のレジスタ/領域を持つ構造 FPGA を定義してみましょう。

struct __attribute__ ((__packed__)) sFPGA {
    uchar   Spare1[0x24];
    ushort  DiscreteInput;
    uchar   Spare2[0x7A];
//CPLD_Version is required to be at offset 0xA0, so 0xA0-0x24-2=0x7A
    ushort  CPLD_Version;
};

今、私は手計算と、構造が変化した場合のエラーの可能性にイライラし、怒っています。これをより堅牢/便利にする方法はありますか? 私はそれをこのように書いてみました:

uchar   Spare2[0xA0 - offsetof(sFPGA, Spare2)];

しかし、これは不完全な構造体について不平を言ってコンパイルしません...私の例は単純化されていることに注意してください。実際には、定義しなければならない予備フィールドが 20 ~ 30 あります。構造は非常に大きいです。

4

4 に答える 4

2

まあ、これはミスユニバース賞を受賞することはありませんが、あなたが望むものだと思います:

#include <boost/preprocessor/cat.hpp>
typedef unsigned char uchar;
typedef unsigned short ushort;
#define PAD_FIELDS(i_,f_, n_) \
    typedef struct __attribute__((packed)) {f_}             ftype##i_; \
    typedef struct __attribute__((packed)) {f_ uchar t_;}   ttype##i_; \
    f_ uchar BOOST_PP_CAT(padding,i_)[n_ - sizeof (BOOST_PP_CAT(ftype,i_)) * (sizeof (BOOST_PP_CAT(ftype,i_)) != sizeof (BOOST_PP_CAT(ttype,i_)))];

struct sFPGA {
    PAD_FIELDS(1,
    PAD_FIELDS(2,
    uchar   Spare1[0x24];
    ushort  DiscreteInput;
    //CPLD_Version is required to be at offset 0xA0
    , 0xA0)        // First padding
    ushort  CPLD_Version;
    uchar   more_stuff[0x50];
    ushort  even_more[4];
    //NID_Version is required to be at offset 0x10A2
    , 0x10A2)      // Second padding
    ushort  NID_Version;
} __attribute__((packed));

int main() {
    printf("CPLD_Version offset %x\n", offsetof(sFPGA,CPLD_Version));
    printf("NID_Version offset %x\n", offsetof(sFPGA,NID_Version));
}

N=20 のパディング フィールドが必要だとします。たとえば、1 から 20 (私の例のように) または 0 から 19 まで、またはあなたを幸せにするものなら何でもPAD_FIELDS(i,、構造の先頭にそれらの N を追加する必要があります。i次に、たとえば追加するパディングが必要な場合は, 0x80)、次のフィールドが構造体の先頭からオフセット 0x80 に配置されることを意味します。

このコードを実行すると、次のテキストが出力されます。

CPLD_Version offset a0
NID_Version offset 10a2

このマクロが機能する方法は、フィールドで構造を定義し、フィールドを組み込み、構造に従って計算されたパディングを追加することです。

boost::preprocessor マジックを気にしない場合はPAD_FIELDS(1,PAD_FIELDS(2,PAD_FIELDS(3,PAD_FIELDS(4,...、最初に全体を自動化する方法を次に示します。

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/comma.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/punctuation/paren.hpp>
typedef unsigned char uchar;
typedef unsigned short ushort;
#define PAD_FIELDS(i_,f_, n_) \
    typedef struct __attribute__((packed)) {f_}             BOOST_PP_CAT(ftype,i_); \
    typedef struct __attribute__((packed)) {f_ uchar t_;}   BOOST_PP_CAT(ttype,i_); \
    f_ uchar BOOST_PP_CAT(padding,i_)[n_ - sizeof (BOOST_PP_CAT(ftype,i_)) * (sizeof (BOOST_PP_CAT(ftype,i_)) != sizeof (BOOST_PP_CAT(ttype,i_)))];
#define PADMAC(z,n,s) PAD_FIELDS BOOST_PP_LPAREN() n BOOST_PP_COMMA()
#define PADREP(n) BOOST_PP_REPEAT(n, PADMAC, junk)
#define FORCE_EVAL(...) __VA_ARGS__
#define CONTAINS_PADDING(n) FORCE_EVAL(PADREP(n)
#define SET_OFFSET(o) BOOST_PP_COMMA() o BOOST_PP_RPAREN()

struct sFPGA {
    CONTAINS_PADDING(2);
    uchar   Spare1[0x24];
    ushort  DiscreteInput;
    //CPLD_Version is required to be at offset 0xA0
    SET_OFFSET(0xA0);
    ushort  CPLD_Version;
    uchar   more_stuff[0x50];
    ushort  even_more[4];
    //NID_Version is required to be at offset 0x10A2
    SET_OFFSET(0x10A2);
    ushort  NID_Version;
    )
} __attribute__((packed));

使用方法の変更点に注意してください。

  1. 構造体の先頭に、必要なパディング要素の数をCONTAINS_PADDING(n)どこに書くかを記述します。n
  2. 構造体の末尾の直前に「)」を追加する必要があります。
  3. ,0x0A)パディングを指定する代わりに、記述する必要がありますSET_OFFSET(0x0A);(これ;はオプションです)。
于 2016-08-14T14:32:41.857 に答える
1

この言語では、特定のパディングを強制することはできません。独自のパディングを追加しても、コンパイラはそれが何をしようとしているのかわかりません。独自の追加のパディングを簡単に追加して、メンバーを必要な方法整列させることができます。

もちろん、特定のCPU、OS、およびコンパイラについては、手動で追加したパディングが希望どおりのパディングになる可能性があるという点で幸運かもしれません --- それを確認するためにテスト プログラムを作成する必要があります。メンバーのオフセットは、あなたが考えているものです。

特定のオフセットでデータに絶対にアクセスする必要がある場合は、非標準の__attribute__(packed)gcc 拡張を試すことができます (ただし、これを参照してください)。または、カスタム I/O ルートを記述してstruct、C プログラミング レベルでのアクセスを容易にするために、データをその形式から逆シリアル化したり、逆シリアル化したりします。

于 2016-08-14T16:20:45.077 に答える
0

これはどうですか:

struct sFPGA {
  struct Internal_S {
    uchar   Spare1[0x24];
    ushort  DiscreteInput;
  } s;
  uchar   Spare2[0xA0 - sizeof (struct Internal_S)];
  ushort  CPLD_Version;
};
于 2016-08-14T12:30:38.787 に答える