3

私はstruct既存のプログラムに非常に大きな影響を与えています。この構造体には、多数のビットフィールドが含まれています。

その一部 (たとえば、150 フィールドのうち 10 フィールド) を保存したいと考えています。

サブクラスを保存するために使用するサンプル コードは次のとおりです。

typedef struct {int a;int b;char c} bigstruct;
typedef struct {int a;char c;} smallstruct;
void substruct(smallstruct *s,bigstruct *b) {
    s->a = b->a;
    s->c = b->c;
}
int save_struct(bigstruct *bs) {
    smallstruct s;
    substruct(&s,bs);
    save_struct(s);
}

また、時々変更したいので、どの部分を選択するのが面倒でないかを願っています. 前に紹介した素朴なアプローチは非常に壊れやすく、維持できません。smallstruct20 の異なるフィールドにスケールアップする場合、 と関数の両方でフィールドを変更する必要がありsubstructます。

私は2つのより良いアプローチを考えました。残念ながら、どちらも外部のCILのようなツールを使用して構造体を解析する必要があります。

最初のアプローチは、substruct関数を自動的に生成することです。の構造体を設定し、それを解析して のフィールドに従って関数smallstructを生成するプログラムを作成します。substructsmallstruct

2 番目のアプローチは、(C パーサーを使用して) に関するメタ情報bigstructを作成し、構造体の特定のフィールドにアクセスできるようにするライブラリを作成することです。これは、Java のクラス リフレクションのアドホックな実装のようなものです。

たとえば、struct-alignment がないと仮定すると、struct に対して

struct st {
    int a;
    char c1:5;
    char c2:3;
    long d;
}

次のメタ情報を生成します。

int field2distance[] = {0,sizeof(int),sizeof(int),sizeof(int)+sizeof(char)}
int field2size[] = {sizeof(int),1,1,sizeof(long)}
int field2bitmask[] =  {0,0x1F,0xE0,0};
char *fieldNames[] = {"a","c1","c2","d"};

この関数でithフィールドを取得します。

long getFieldData(void *strct,int i) {
    int distance = field2distance[i];
    int size = field2size[i];
    int bitmask = field2bitmask[i];
    void *ptr = ((char *)strct + distance);
    long result;
    switch (size) {
        case 1: //char
             result = *(char*)ptr;
             break;
        case 2: //short
             result = *(short*)ptr;
        ...
    }
    if (bitmask == 0) return result;
    return (result & bitmask) >> num_of_trailing_zeros(bitmask);
 }

どちらの方法も余分な作業が必要ですが、パーサーがメイクファイルに含まれていれば、サブ構造体の変更は簡単です。

ただし、外部依存関係なしでそれを行いたいと思います。

誰もがより良いアイデアを持っていますか? 私のアイデアが良いところで、インターネット上で私のアイデアの利用可能な実装はありますか?

4

5 に答える 5

12

あなたの説明から、元の構造にアクセスして変更できるようです。サブ構造を完全な型にリファクタリングし (例で行ったように)、その構造を大きな構造のフィールドにして、元の構造のすべてのフィールドを小さな構造にカプセル化することをお勧めします。

あなたの小さな例を拡張する:

typedef struct 
{
  int a;
  char c;
} smallstruct;

typedef struct 
{
  int b;
  smallstruct mysub;
} bigstruct;

smallstruct 情報にアクセスするには、次のようにします。

/* stack-based allocation */
bigstruct mybig;
mybig.mysub.a = 1;
mybig.mysub.c = '1';
mybig.b = 2;

/* heap-based allocation */
bigstruct * mybig = (bigstruct *)malloc(sizeof(bigstruct));
mybig->mysub.a = 1;
mybig->mysub.c = '1';
mybig->b = 2;

ただし、小さな構造体へのポインターを渡すこともできます。

void dosomething(smallstruct * small)
{ 
  small->a = 3;
  small->c = '3';
}

/* stack based */    
dosomething(&(mybig.mysub));

/* heap based */    
dosomething(&((*mybig).mysub));

利点:

  • マクロなし
  • 外部依存なし
  • 記憶順キャストハックなし
  • よりクリーンで読みやすく、使いやすいコード。
于 2009-05-19T14:14:54.703 に答える
3

フィールドの順序を変更することが問題外でない場合は、smallstruct フィールドが一緒になるように bigstruct フィールドを再配置することができます。その後、単純に 1 つのフィールドから別のフィールドにキャストするだけです (オフセットを追加する可能性があります)。 . 何かのようなもの:

typedef struct {int a;char c;int b;} bigstruct;
typedef struct {int a;char c;} smallstruct;

int save_struct(bigstruct *bs) {
    save_struct((smallstruct *)bs);
}
于 2009-05-19T14:11:54.210 に答える
1

マクロはあなたの友達です。

解決策の 1 つは、大きな構造体を独自のインクルード ファイルに移動してから、マクロ パーティを作成することです。

通常構造を定義する代わりに、BEGIN_STRUCTURE、END_STRUCTURE、NORMAL_FIELD、SUBSET_FIELD などのマクロを選択します。

その後、ファイルを数回インクルードし、パスごとにそれらの構造を再定義できます。最初のものは定義を通常の構造に変換し、両方のタイプのフィールドが通常として出力されます。2 番目は、NORMAL_FIELD には何もないと定義し、サブセットを作成します。3 番目は、サブセット フィールドをコピーするための適切なコードを作成します。

最終的には、構造の単一の定義が得られます。これにより、サブセットに含まれるフィールドを制御し、適切なコードを自動的に作成できます。

于 2009-05-19T14:13:55.173 に答える
0

このアプローチを取ることをお勧めします:

  1. 大きな構造を書いた男をのろいなさい。ブードゥー人形を手に入れて楽しんでください。
  2. 何らかの形で必要な大きな構造の各フィールドにマークを付けます(マクロまたはコメントなど)
  3. ヘッダーファイルを読み取り、マークされたフィールドを抽出する小さなツールを作成します。コメントを使用する場合は、各フィールドに優先順位などを付けて並べ替えることができます。
  4. 下部構造の新しいヘッダーファイルを書き込みます(固定ヘッダーとフッターを使用)。
  5. createSubStruct大きな構造体へのポインターを受け取り、サブ構造体へのポインターを返す関数を含む新しいCファイルを作成します
  6. この関数で、収集されて出力されるフィールドをループしますss.field = bs.field(つまり、フィールドを1つずつコピーします)。
  7. 小さなツールをmakefileに追加し、新しいヘッダーとCソースファイルをビルドに追加します

gawkツールとして、または使い慣れたスクリプト言語を使用することをお勧めします。ビルドには30分かかるはずです。

[編集]本当にリフレクションを試してみたい場合(これは反対です。Cで機能させるにはかなりの作業が必要になります)、offsetof()マクロはあなたの友達です。このマクロは、構造内のフィールドのオフセットを返します(ほとんどの場合、その前のフィールドのサイズの合計ではありません)。この記事を参照してください。

[EDIT2]独自のパーサーを作成しないでください。独自のパーサーを正しく取得するには、数か月かかります。私は私の人生でたくさんのパーサーを書いたので知っています。代わりに、コピーする必要のある元のヘッダーファイルの部分にマークを付けてから、機能することがわかっている1つのパーサー(Cコンパイラーの1つ)に依存します。これを機能させる方法のいくつかのアイデアがあります:

struct big_struct {
    /**BEGIN_COPY*/
    int i;
    int j : 3;
    int k : 2;
    char * str;
    /**END_COPY*/
    ...
    struct x y; /**COPY_STRUCT*/
}

/**BEGIN_COPY*/ツールにとの間の何かをコピーさせるだけです/**END_COPY*/

割り当ての代わりに/**COPY_STRUCT*/生成するようにツールに指示するなどの特別なコメントを使用します。memcpy()

これは、数時間で作成およびデバッグできます。機能なしでCのパーサーをセットアップするには、同じくらい時間がかかります。つまり、有効なCを読み取ることができるものがあれば、Cを理解するパーサーの部分と、データを処理する部分を記述する必要があります。

于 2009-05-19T14:45:55.013 に答える
0

メタデータの取得を支援するために、offsetof() マクロを参照できます。これには、パディングを処理できるという利点もあります。

于 2009-05-19T14:17:54.760 に答える