dimanche 28 décembre 2014

Implementing Recursive Proxy pattern in C++11

Suppose we have some Foo object that allows:



cout << myFoo[3];
myFoo[5] = "bar";


This calls for a proxy design-pattern (detailed by Scott Meyers -- link)


But now let us suppose every myFoo[i] is also a Foo instance.



myFoo[7] = Foo{...};
myFoo[5] = "bar"; // Foo has a Foo(std::string) non-explicit constructor


I'm close to having an implementation, but I can't get rid of one final pesky "forward declaration/ incomplete type" error.


Firstly, let's get the easy one out of the way:



// 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) };
}


Here is the basic nonrecursive proxy pattern:



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
};


Not sure if I need that last handler:


Anyway, making it recursive, we would require



class Proxy : Object {
:


And this means that we can no longer define Proxy within Object, otherwise we will get an "attempting to base from incomplete type" compiler error.


But this is where it gets gnarly.


If we move Object::Proxy's definition outside of (after, in fact) Object, the original



Proxy operator [] ( const Object& key ) { return Proxy{ *this, key }; }


... now gives us an error because we have made use of an incomplete class (Proxy).


It appears to be a Catch-22, and I can't see any clean solution.


Is there one?


Aucun commentaire:

Enregistrer un commentaire