4

私は C++ で Python 3 拡張機能を作成してPyObjectいます。インスタンス レイアウトを定義する型 (構造体) に a が関連しているかどうかを確認する方法を見つけようとしています。PyObjectではなく、 static-size にのみ興味がありますPyVarObject。インスタンス レイアウトは、明確に定義された特定のレイアウト (必須PyObjectヘッダーと (オプション) ユーザー定義メンバー) を持つ構造体によって定義されます。

以下は、Defining New Types のよく知られた Noddy の例にPyObject基づく拡張の例です。

// Noddy struct specifies PyObject instance layout
struct Noddy {
    PyObject_HEAD
    int number;
};

// type object corresponding to Noddy instance layout
PyTypeObject NoddyType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "noddy.Noddy",             /*tp_name*/
    sizeof(Noddy),             /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    ...
    Noddy_new,                 /* tp_new */
};

Noddyは型であり、コンパイル時のエンティティですがNoddyType、実行時にメモリ内に存在するオブジェクトであることに注意してください。Noddyとの間の唯一の明らかな関係は、メンバーに格納されNoddyTypeている値のようです。sizeof(Noddy)tp_basicsize

Python で実装された手書きの継承PyObjectは、特定の のインスタンス レイアウトを宣言するために使用される と タイプの間でキャストできるようにするルールを指定しPyObjectます。

PyObject* Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    // When a Python object is a Noddy instance,
    // its PyObject* pointer can be safely cast to Noddy
    Noddy *self = reinterpret_cast<Noddy*>(type->tp_alloc(type, 0));

    self->number = 0; // initialise Noddy members

    return reinterpret_cast<PyObject*>(self);
}

さまざまなスロット関数のような状況では、「Python オブジェクトは Noddy である」と想定して、チェックなしでキャストしても問題ありません。ただし、他の状況でキャストする必要がある場合があり、ブラインド変換のように感じます。

void foo(PyObject* obj)
{
    // How to perform safety checks?
    Noddy* noddy = reinterpret_cast<Noddy*>(obj);
    ...
}

を確認することはできますがsizeof(Noddy) == Py_TYPE(obj)->tp_basicsize、次の理由により解決策としては不十分です。

1) ユーザーが派生する場合Noddy

class BabyNoddy(Noddy):
    pass

とのインスタンスへのポイントはobj異なります。ただし、インスタンス レイアウト パーツへのポインターを取得するためにキャストしても安全です。fooBabyNoddyPy_TYPE(obj)->tp_basicsizereinterpret_cast<Noddy*>(obj)

2) と同じサイズのインスタンス レイアウトを宣言する他の構造体が存在する可能性がありますNoddy

struct NeverSeenNoddy {
    PyObject_HEAD
    short word1;
    short word2;
};

実際、C 言語レベルでは、構造体は型オブジェクトNeverSeenNoddyと互換性があり、. したがって、キャストはまったく問題ありません。NoddyTypeNoddyType

だから、私の大きな質問はこれです:

がインスタンス レイアウトPyObjectと互換性があるかどうかを判断するために使用できる Python ポリシーはありますか?Noddy

PyObject*に埋め込まれているオブジェクト部分を指しているかどうかを確認する方法はありNoddyますか?

ポリシーではない場合、ハッキングの可能性はありますか?

編集:似ているように見える質問がいくつかありますが、私の意見では、私が尋ねたものとは異なります。例: PyObject の基になる構造体へのアクセス

EDIT2: Sven Marnach の回答を回答としてマークした理由を理解するには、その回答の下のコメントを参照してください。

4

2 に答える 2

5

Python では、 test を使用して、 が型であるか派生型であるかを確認objできNoddyますisinstance(obj, Noddy)PyObject *obj一部が型であるか派生型であるかの C-API でのテストは、NoddyType基本的に同じです。次を使用しますPyObject_IsInstance()

PyObject_IsInstance(obj, &NoddyType)

2 番目の質問については、これを達成する方法はありません。これが必要だと思う場合、設計には深刻な欠点があります。そもそもNeverSeenNoddyTypeから派生する方がよいでしょう。そうすると、上記のチェックでも、派生型のオブジェクトが のインスタンスとして認識されます。NoddyTypeNoddyType

于 2011-12-11T00:46:38.413 に答える
1

すべてのオブジェクトは で始まるPyObject_HEADため、このヘッダーで定義されたフィールドにアクセスすることは常に安全です。フィールドの 1 つ(通常はマクロob_typeを使用してアクセス) です。Py_TYPEthis が を指している場合、NoddyTypeまたは派生した他の型NoddyType(これが示すものPyObject_IsInstanceです) である場合、オブジェクトのレイアウトは のレイアウトであると想定できますstruct Noddy

つまり、オブジェクトがそのサブクラスを指してNoddyいる場合、そのオブジェクトはインスタンス レイアウトと互換性があります。Py_TYPENoddyType

2番目の質問では、キャストはうまくいきません。サイズは同じでも、Noddyとのレイアウトは異なります。NeverSeenNoddy

NeverSeenNoddyそれがタイプのレイアウトであると仮定すると、ifが falseNeverSeenNoddy_Typeにキャストするべきではありません。NeverSeenNoddyPyObject_IsInstance(obj, &NeverSeenNoddy_Type)

共通フィールドを持つ 2 つの C レベル型が必要な場合は、インスタンス レイアウトに共通フィールドのみを持つ共通ベースから両方の型を派生させる必要があります。

次に、サブタイプのレイアウトの上部にベース レイアウトを含める必要があります。

struct SubNoddy {
    // No PyObject_HEAD because it's already in Noddy
    Noddy noddy;
    int extra_field;
};

次に、PyObject_IsInstance(obj, &SubNoddy_Type)true が返された場合は、フィールドにキャストしSubNoddyてアクセスできextra_fieldます。true を返す場合は、共通フィールドにPyObject_IsInstance(obj, &Noddy_Type)キャストしてアクセスできます。Noddy

于 2011-12-11T02:30:02.783 に答える