2012-04-29 9 views
0

Cに関する質問があり、構造体のメンバーにアクセスする場合にのみ、 "継承"の部分型を模倣しようとしています。次の例を見てください:Cの構造体のメンバーにアクセスする際の "継承"の模擬

#pragma pack(push,1) 
typedef struct foo 
{ 
    int value; 
    int value2; 
}foo; 
typedef struct foo_extended 
{ 
    // "inherits" foo 
    int value; 
    int value2; 
    // "inherits" foo stops 
    //we also have some additional data 
    float additional; 
}foo_extended; 
#pragma pack(pop) 


//! This function works for both foo types 
void workboth(void* objP) 
{ 
    foo* obj = (foo*)objP; 

    obj->value = 5; 
    obj->value2 = 15; 
} 

//! This works only for the extended 
void workextended(foo_extended* obj) 
{ 
    obj->value = 25; 
    obj->value2 = 35; 
    obj->additional = 3.14; 
} 

int main() 
{ 
    foo a; 
    foo_extended b; 

    workboth(&a); 
    workboth(&b); 
    workextended(&b); 

    return 0; 
} 

これが私のシステムで動作しますが、私の質問はこれがある限り関与構造(コンパイラに依存する)の正しいパッキングがあるとして、ポータブルことができるかどうかです。 #ifndefsを別のコンパイラでもきちんと詰め込んで呼び出す必要があると思います。

もちろん明らかな問題は、型チェックの完全な欠如と正しい使用法の責任のすべてをプログラマーに任せていることですが、移植性があるかどうかは疑問です。ありがとう!

P.Sは:私が付着しようとすると、標準のは、私は少し異なる方法を持っているC99

答えて

0

C99 draftの6.7.2.1/12と/ 13を読むことで、同じ初期メンバーを持つ2つの異なる構造が最初の異なるメンバーまで互換性があると考えることができると思います。

6.7.2.1/12

構造または共用オブジェクトの各非ビットフィールドメンバは、その種類に適切な実装定義の方法で整列されます。ビットフィールドは、それらが宣言された順に増加アドレスを持って存在する構造物、非ビットフィールドメンバーとユニット内

6.7.2.1/13

。適切に変換された構造体オブジェクトへのポインタは、その最初のメンバ(またはそのメンバがビットフィールドの場合はそれが存在するユニット)を指し、逆の場合も同様です。構造体オブジェクト内に無名のパディングがあるかもしれませんが、最初はありません。

C11のよう
+0

ありがとうございます。私はC99と互換性があるので、これは私の質問に答えます。私の質問で言及するのを忘れた – Lefteris

+0

うーん、私はあなたが引用しているテキストからあなたの結論を引き出す方法を参照してくださいない。それらの順序が同じで、アラインメントがその型に適切であることだけを保証します。フィールドが異なると、 'foo'の内部パディングが変更されないことは何も言いません。 –

+0

はい、もちろん@JensGustedt。しかし、実装者は、構造的に異なる実装の詳細を持つためにはプログラマが積極的にプログラマになる必要があります。例えば、2人または3人のmenbersと言うと... – pmg

1

であることを言及し忘れました。代わりに同じ値を使用して、私は構造体の構造体を作成し、この方法は、パッキンは不要です。

void workextended(foo_extended* obj) 
{ 
    obj->father.value = 25; 
    obj->father.value2 = 35; 
    obj->additional = 3.14; 
} 
:あなたが示したよう

typedef struct foo 
{ 
    int value; 
    int value2; 
}foo; 

typedef struct foo_extended 
{ 
    foo father; 
    float additional; 
}foo_extended; 

は今、残りは小さな違いで、かなり多くあります

しかし、正しいオブジェクトにキャストが行われていることを確認するために、階層内の最初のオブジェクトのフィールドとしてIDを追加します。

このメソッドはC標準で動作することが保証されています。

+0

答えをありがとう。私はこの方法を知っていますが、その方法の問題は、あなたが示したようにobj-> father.valueを行う必要があることです。 – Lefteris

1

、また、古い規格の拡張など、いくつかの既存のコンパイラでサポートされている、あなたはあなたの下部が内fooとまったく同じレイアウトを持っている。このことにより、その

struct foo_extended { 
    struct { 
     int value; 
     int value2; 
    }; 
    //we also have some additional data 
    float additional; 
}; 

の匿名structを使用する必要があります特に、その部分の整列に関係するもの:異なるコンパイル単位structの間で互換性があり、同じ順序で全く同じフィールドを持つものは、同じように配置する必要があります。

(パックされたプラグマの影響が私にはそれほど明確ではない)

あなたfoo構造が、それは常に、その1つの内0をオフセットする必要があります​​で初めてですので。

+0

これは私が推測する最良の選択です。問題はC11だということです。互換性の理由から私は自分の方法に固執すべきだと思う。ここの考え方が間違っていると私を訂正してください。 パックされたプラグマは、そこに置かなければコンパイラがこれらの構造を別々にパックするので、ポインタの逆参照時に間違ったデータを取得することがあります。 – Lefteris

+0

私はC11がここでの制限が大きすぎるとは思わない。 gcc(+親族)とMicrosoftコンパイラがこれをサポートしています。だからあなたは主に覆われるだろう:) –

+0

私はそれについて疑問に思っていた返信の理由でそれを言いました。ああ、これは、すべての構造体を定義するだけの小さな変更だから、将来容易に実行できるようにする必要があります。それについてもう少し調べる必要があります。 迷惑なパッキングプラグマを取り除くのに役立つでしょうか – Lefteris

関連する問題