以下を可能にするFoo
オブジェクトがあるとします。
cout << myFoo[3];
myFoo[5] = "bar";
これには、プロキシの設計パターンが必要です ( Scott Meyers による詳細はこちら) 。
しかしここで、everymyFoo[i]
もFoo
インスタンスであるとしましょう。
myFoo[7] = Foo{...};
myFoo[5] = "bar"; // Foo has a Foo(std::string) non-explicit constructor
私は実装に近づいていますが、最後の厄介な「前方宣言/不完全型」エラーを取り除くことはできません。
まず、簡単なものから始めましょう。
// x = someConstObject[4], so this must be Rvalue access
// i.e. someConstObject[4] = ... would be a contradiction / const violation
const Object operator[] (const Object& key) const {
return Object{ PyObject_GetItem(p, key.p) };
}
基本的な非再帰プロキシ パターンは次のとおりです。
Proxy operator [] ( const Object& key ) { return Proxy{ *this, key }; }
class Proxy {
private:
const Object& container;
const Object& key;
public:
// at this moment we don't know whether it is 'container[key] = x' or 'x = container[key]'
Proxy( const Object& c, const Object& k ) : container{c}, key{k}
{ }
// Rvalue
// e.g. cout << myList[5]
operator Object() const {
return container[key]; // <-- invokes the original const [] overload
}
// Lvalue
// e.g. myList[5] = foo
const Object& operator= (const Object& rhs_ob) {
PyObject_SetItem( container.p, key.p, rhs_ob.p );
return rhs_ob; // allow daisy-chaining a = b = c etc.
}
#if 0
// I think this should come for free, as the above Rvalue handler
// ... collapses a Proxy into an Object
// e.g. myList[5] = someOtherList[7]
const Proxy& operator= (const Proxy& rhs) {
// Force resolution of rhs into Object
PyObject_SetItem( pContainerObj->p, pKeyObject->p, static_cast<Object>(rhs).p /* rhs.value->p*/ );
return rhs;
}
#endif
// ^ Note: allows:
// e.g. x = y[1] = z[2]; // <-- y[1] must return an Object
// e.g. if( y[1] = z[2] ) // <-- assigns and then checks that y[1] evaluates to true
};
その最後のハンドラーが必要かどうかわかりません。
とにかく、再帰的にするには、次のものが必要です。
class Proxy : Object {
:
Proxy
これは、内で定義できなくなったことを意味しますObject
。そうしないと、「不完全な型をベースにしようとしています」というコンパイラ エラーが発生します。
では、そうしましょう。また、可能な場合はコンストラクターを変更して基本クラスを埋める必要があります。
class Object::Proxy : public Object {
private:
const Object& container;
const Object& key;
public:
// at this moment we don't know whether it is 'c[k] = x' or 'x = c[k]'
// If it's 'c[k] = x', setting the base class to c[k] is going to
// either set it to the old value of c[k]
// or a None object (if it didn't have any value previously)
// we had better be certain to make sure the original c[k] overload
// returns None if unsuccessful
Proxy( const Object& c, const Object& k )
: container{c}, key{k}, Object{c[k]} // <-- might fail!
{ }
そして、Object
基本クラスにより、typecast-to-object を手動で処理する必要がなくなります。
// Rvalue
// e.g. cout << myList[5] hits 'const Object operator[]'
#if 0
// it looks as though we don't need to do this given that
// we now have Object as base class
operator Object() const {
return container[key];
}
#endif
しかし、これはそれが危険になるところです。
Object::Proxy
の定義を (実際には後) の外に移動するObject
と、元の
Proxy operator [] ( const Object& key ) { return Proxy{ *this, key }; }
... 不完全なクラス ( Proxy
) を使用しているため、エラーが発生します。単に定義を外側に移動しても、戻り値の型が であるという事実は修正されないことに注意してくださいProxy
。それができればProxy*
。しかし、Proxy
できません。
これは Catch-22 のように見えますが、明確な解決策は見当たりません。
ありますか?
編集: 欠陥のある設計を示唆するコメントに応じてObject
、ポインターの軽量ラッパーであることに注意してください。PyObject*
データ メンバーは 1 つだけです。
編集:私が取り組んでいる元のコードはここにあります