2011-02-08 11 views
7

メンバ変数を持つことは可能ですか?それを含むオブジェクトへのポインタを自分自身へのポインタから計算することは可能ですか?C++クラスメンバ変数自身のオフセットを知っています

のは、このようなAPIに包まれた外国人のコール・インタフェースを持ってみましょう:外国側の各クラスについては0からNまでの引数の他の番号について

template <typename Class, MethodId Id, typename Signature> 
class MethodProxy; 

template <typename Class, MethodId Id, typename ReturnT, typename Arg1T> 
class MethodProxy<Class, Id, ReturnT()(Arg1T) { 
    public: 
    ReturnT operator()(Class &invocant, Arg1T arg1); 
}; 

と同様に、一つのC++クラスがいくつかで宣言されていますこのテンプレートはこれらの特性(および引数型のより多くの特性)を使用して、外部メソッドを検索して呼び出します。これは次のように使用することができます署名を複数回繰り返すことなく

Foo foo; 
foo.bar(5); 

:今、私がやりたいものを

Foo foo; 
MethodProxy<Foo, barId, void()(int)> bar; 
bar(foo, 5); 

私は次のように呼び出すことができるような方法でFooを定義しています。(明らかに静的メンバーを作成し、メソッドで呼び出しをラップするのは簡単です、右)。実際、それはまだ簡単です:

template <typename Class, MethodId Id, typename Signature> 
class MethodMember; 
template <typename Class, MethodId Id, typename ReturnT, typename Arg1T> 
class MethodMember<Class, Id, ReturnT()(Arg1T) { 
    MethodProxy<Class, Id, Signature> method; 
    Class &owner; 
    public: 
    MethodMember(Class &owner) : owner(owner) {} 
    ReturnT operator()(Arg1T arg1) { return method(owner, arg1); } 
}; 

しかし、オブジェクトは多くの場合、それ自身へのポインタのコピーを多数含んでいます。だから私は、これらのインスタンスがthisといくつかの追加のテンプレート引数から所有者のポインタを計算できるようにする方法を探しています。

私は

template <typename Class, size_t Offset, ...> 
class Member { 
    Class *owner() { 
     return reinterpret_cast<Class *>(
      reinterpret_cast<char *>(this) - Offset); 
    } 
    ... 
}; 
class Foo { 
    Member<Foo, offsetof(Foo, member), ...> member; 
    ... 
}; 

の線に沿って考えていたが、これはFooのがポイントで不完全な型であることを不平を言います。

はい、私はoffsetofが "POD"タイプでしか動作しないと考えていますが、これが実際に動作する非仮想メンバーであれば実際に動作します。私は同様に、その引数にポインタ型(それ)のメンバ(ダミーのベースクラスを使用)を渡そうとしましたが、どちらも動作しません。

これが機能すれば、それを含むクラスのメソッドに委譲するC#のようなプロパティを実装することもできます。

私はboost.preprocessorで上記のラッパーメソッドを実行する方法を知っていますが、引数リストは奇妙な形式で指定する必要があります。私は、テンプレートを使ってジェネリックラッパーを生成するためにマクロを書く方法を知っていますが、それはおそらく貧弱な診断を与えるでしょう。呼び出しがfoo.bar()(5)のように見える場合は、それも簡単です。しかし、私はいくつかの巧妙なトリックが可能であるかどうかを知りたがっています(しかもそのような巧妙なトリックだけがおそらくプロパティにも使えるでしょう)。

注:メンバ型は、オフセットを割り当てることができる前に型を知っていなければならないため、メンバ型へのメンバポインタまたはオフセット型のいずれにも特化できません。これは、型が必要な配置に影響する可能性があるためです(明示的/派生的な特殊化を考慮)。

+0

は、私はそれを数回を読んで、まだあなたが何をしたいのか得ることはありません、あなたは認識しているジェネリック*不動産*クラスをしたいですかそれを所有するもの - もしそうなら、なぜそれを所有しているのかを知る必要がありますか?私は本当に必要とされるすべての財産が価値を受け入れて価値を返す能力であると想像すべきですか? – Nim

+1

私はそれを取得しません..あなたは何をしようとしているのですか? – mfontanini

+0

@ニム:はい、それを所有するものを認識している一般的な「プロパティ」クラスが必要です。プロパティーの場合は、プロパティー値を* computed *にする必要があります。私の場合、それは基本的なメソッドへのポインタを所有者に渡す必要があるファンクタです。 –

答えて

6

それがより一般的にする方法を考えて、1 MS固有のソリューションです答えを実現するための最良の方法ですので、私はきた場所です得点:

オフセットはテンプレートの引数にすることはできません。オフセットを計算する前に型を知っている必要があるからです。したがって、引数の関数によって返される必要があります。タグタイプ(ダミー構造体)を追加し、オーバーロードされた関数をOwnerに入れるか、直接タグに追加してみましょう。こうすることで、必要なものをすべて1か所(マクロを使用して)で定義できます。次のコードは、すべてのメンバーのためのgcc 4.4.5およびプリント正しいポインタで罰金コンパイル:

#include <cstddef> 
#include <iostream> 

using namespace std; 

(ただプリアンブルはそれが本当にコンパイルするために)

template <typename Owner, typename Tag> 
struct offset_aware 
{ 
    Owner *owner() 
    { 
     return reinterpret_cast<Owner *>(
      reinterpret_cast<char *>(this) - Tag::offset()); 
    } 
}; 

これは、オブジェクトを認識させるために必要なものですそれは自分自身のオフセットです。プロパティやファンクタ、または他のコードを自由に追加して有用にすることができます。今、私たちは、部材自体と一緒にいくつかの余分なものを宣言する必要があり、それでは、このマクロを定義してみましょう:

#define OFFSET_AWARE(Owner, name) \ 
    struct name ## _tag { \ 
     static ptrdiff_t offset() { \ 
      return offsetof(Owner, name); \ 
     } \ 
    }; \ 
    offset_aware<Owner, name ## _tag> name 

これは、必要なオフセットを返す関数内のタグとプットとしての構造を定義します。それはデータメンバー自体を定義するよりも。

ここで定義されているようにメンバをパブリックにする必要がありますが、タグの保護されたプロパティとプライベートプロパティの 'friend'宣言を簡単に追加できます。さて、それを使ってみましょう。

struct foo 
{ 
    int x; 
    OFFSET_AWARE(foo, a); 
    OFFSET_AWARE(foo, b); 
    OFFSET_AWARE(foo, c); 
    int y; 
}; 

シンプルではないですか?

int main() 
{ 
    foo f; 

    cout << "foo f = " << &f << endl 
     << "f.a: owner = " << f.a.owner() << endl 
     << "f.b: owner = " << f.b.owner() << endl 
     << "f.c: owner = " << f.c.owner() << endl; 
    return 0; 
} 

これは、すべての行に同じポインタ値を出力します。 C++標準では、メンバーのサイズは0にはできませんが、実際のコンテンツのサイズのみ、または空の場合は1バイト(プラットフォームに応じて)と比較して1バイトです。

+0

誰かが 'foo'からポリモーフィッククラスを継承すると、この解決策は破られることに注意してください。 –

+0

@マークB:そうでしょうか? 'foo'がどのように由来しても、メンバは' foo'サブインスタンスと同じオフセットにありますので、 'owner()'はそのサブインスタンスへのポインタを返します。ポイント。 –

+0

@JanHudec、それは半年後の素晴らしいイベントです。ありがとう。 – Kumputer

0

実際には、呼び出しに含まれるオブジェクトへの参照が必要であると仮定すると、所有者への参照を格納するだけです。余分な参照を格納するためにメモリが大幅に増加しているという特定のメモリプロファイリングの証拠がない限り、それを明白な方法で実行してください。今の

+1

質問を読んだことがありますか?それはちょうど真ん中のオプションがしていることです、そして、私はその参照なしで得る方法があるかどうか尋ねました。 –

+1

@Jan Hudecなぜ余分な参照は修正する必要がある実際の問題だと思いますか?私の最初の質問は、ここに実際の問題がないかもしれないということです。 –

+2

さて、問題は具体的には、それを持たない解決策を求めています。彼らに問題があるかどうかは問題ではありません。—私はそれらを特に除外しました。そうでなければ、問題は簡単で議論の余地がないからです。 –

1

は、まだここに質問をする

#include <stdio.h> 

#define offs(s,m) (size_t)&(((s *)0)->m) 
#define Child(A,B,y) \ 
    __if_exists(X::y) { enum{ d_##y=offs(X,y) }; } \ 
    __if_not_exists(X::y) { enum{ d_##y=0 }; } \ 
    B<A,d_##y> y; 

template <class A, int x> 
struct B { 
    int z; 
    void f(void) { 
    printf("x=%i\n", x); 
    } 
}; 

template< class X > 
struct A { 
    int x0; 
    int x1; 
    Child(A,B,y); 
    Child(A,B,z); 
}; 

typedef A<int> A0; 

typedef A<A0> A1; 

int main(void) { 
    A1 a; 
    a.y.f(); 
    a.z.f(); 
} 
+0

__if_exists/__ if_not_existsは必要ありません。とにかくそれが存在しなければエラーです。そして、それはそこにある唯一のMS特有のものですよね? –

+0

私はちょっと微調整しました(基本的に 'offs'を' 'から 'offsetof'に置き換えましたが、gccは宣言される前にメンバへの参照を許可しないのでコンパイルを拒否しています。 –

+0

クラスは最初にオフセットなしで定義され、 は再び最初のインスタンスからのオフセットを使用して定義されるという考えがあります。 その操作は簡単ですしかし、私はまだ のSFINAEを設定する方法がわかりません(operator-> or - > *を再定義することは約束していたようですが、 が動作しませんでした) 。__if_existsなしでは動作しません。 – Shelwien

2

1)フィッティング見えたgccの拡張機能があります:マニュアルは
は、「組み込み関数がありませんでした式を評価しないと言うにもかかわらず、予想通り

enum{ d_y = __builtin_choose_expr(N,offsetof(X,y),0) }; 

しかし、それは動作しませんでしたが、選択された "

2)メンバポインタは面白いと思われました。 offsetofはこのように定義することができます

template< class C, class T > 
int f(T C::*q) { 
    return (int)&((*(C*)0).*q); 
} 

しかし、私はまだconstexprのにこれを有効にする方法を見つけることができませんでした。今の

3)は、ここに別のバージョンです:

#include <stdio.h> 

#pragma pack(1) 

template <class A, int x> 
struct B { 
    int z; 
    void f(void) { 
    printf("x=%i\n", x); 
    } 
}; 

#define STRUCT(A) template< int N=0 > struct A { 
#define CHILD(A, N, B, y) }; template<> struct A<N> : A<N-1> \ 
    { B<A<N>,sizeof(A<N-1>)> y; 
#define STREND }; 

STRUCT(A) 
    int x0; 
    int x1; 
    CHILD(A,1, B, y); 
    short x2; 
    CHILD(A,2, B, z); 
    char x3; 
STREND 

typedef A<2> A1; 

int main(void) { 
    A1 a; 
    a.y.f(); 
    a.z.f(); 
} 
+0

選択されていない式は関係ありません。メンバは常に存在し、分岐のオフセットを選択する必要があります。時間内に評価することはできません - (インライン)関数を使用する必要があります。 –

+0

純粋なコンパイル時の方法が存在しないという証拠はまだありません(実際には2をすでに掲載しています)。 – Shelwien

関連する問題