在 C++11 中实现递归代理模式

2024-02-19

假设我们有一些Foo允许的对象:

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

这需要代理设计模式(斯科特·迈耶斯 (Scott Meyers) 详细介绍如下 https://stackoverflow.com/questions/26529354/does-c11s-r-value-reference-operator-obsolete-the-proxy-object-design)

但现在让我们假设每个myFoo[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 within 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基类,我们将不再需要手动处理类型转换为对象:

    // 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 cannot.

这似乎是第 22 条军规,我看不到任何干净的解决方案。

有吗?

编辑:为了回应暗示设计有缺陷的评论,请记住Object是一个指针的轻量级包装。它只有一个PyObject*数据成员。

编辑:我正在工作的原始代码被发现here http://sourceforge.net/p/cxx/code/HEAD/tree/trunk/CXX/CXX/Python3/Objects.hxx#l1084


你的前提似乎有缺陷。 AProxy is not an Object,根据定义;如果是的话,那么你就不会称其为Proxy首先。然后你可以在没有代理的情况下解决你的问题,就像标准数据类型一样std::map解决它:只要有operator[]返回对新创建的引用Object必要时。

你正在寻找类似的东西std::vector<bool>的代理模式:operator[]返回一个Proxyoperator=以及到非代理的隐式转换Object(对于您确实想使用该值而不是分配给它的情况)。

class Object {
    struct Proxy {
        PyObject *container;
        PyObject *key;
        Proxy(PyObject *c, PyObject *k): container(c), key(k) {}
        Proxy& operator= (const Object& value) { 
            PyObject_SetItem(container, key, value.p); 
            return *this; 
        }
        operator Object() const {
            PyObject *p = PyObject_GetItem(container, key);
            if (p == nullptr) throw "proxy was not backed by a real object";
            return p;
        }
    };

    PyObject *p;
    Object(PyObject* p): p(p) {}

public:
    Object operator[] (const Object& key) const { 
        return PyObject_GetItem(p, key.p); 
    }
    Proxy operator[] (const Object& key) { return {p, key.p}; }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C++11 中实现递归代理模式 的相关文章

随机推荐