过载 - >运营商通过代理转发成员访问

时间:2014-12-29 12:40:57

标签: c++ c++11 operator-overloading proxy-pattern

我试图在PyObject*类中包装Python Object。 在Python中,一切都是PyObject*。 列表是PyObject*,列表中的每个项目本身都是PyObject*。 哪个甚至可能是另一个列表。 等

我试图通过代理模式(here)来允许fooList[42] = barObj样式语法。

现在我已经开始工作,我想扩展它,以便fooList[42]可以用作Object。具体来说,我希望能够处理......

fooList[42].myObjMethod()
fooList[42].myObjMember = ...

Object有很多方法,目前fooList[42].myObjMethod()首先将fooList[42]解析为Proxy个实例,比如tmpProxy,然后再尝试tmpProxy.myObjMethod()

这意味着我必须这样做

void Proxy::myObjMethod(){ return wrapped_ob.myObjMethod(); }

即。通过Object手动转发每个Proxy的方法,这很丑陋。

我无法看到任何完美的解决方案(请参阅上面的链接答案),但我很乐意使用:

fooList[42]->myObjMethod()

...作为折衷方案,视为 - > 可以 重载(而不是.,但不能)。

但是,我找不到任何重载operator->的文档。

我最好的猜测是它必须返回指向某个对象的指针(比如说pObj),而C ++会调用pObj->whatever


以下是我的尝试实施。但是,我遇到了一个'带着对象' 类型的临时对象的地址警告。

我在Object课程中

const Object operator[] (const Object& key)     const { 
    return Object{ PyObject_GetItem( p, key.p ) }; 
}

注意' const Object&'遇到'取一个Object' 类型的临时对象的地址。

class Proxy {
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]'
    Proxy( const Object& c, const Object& k ) : container{c}, key{k}
    { }

    // Rvalue
    // e.g. cout << myList[5] hits 'const Object operator[]'
    operator Object() const {
        return container[key];
    }

    // Lvalue
    // e.g. (something = ) myList[5] = foo
    const Proxy&  operator= (const Object& rhs_ob) {
        PyObject_SetItem( container.p, key.p, rhs_ob.p );
        return *this; // allow daisy-chaining a = b = c etc, that's why we return const Object&
    }

    const Object* operator->() const { return &container[key]; }
    // ^ ERROR: taking the address of a temporary object of type Object
};

这个想法是允许myList[5]->someMemberObj = ...样式语法。

myList[5]解析为Proxy实例,其中包含ObjectmyList的第六个元素)。我们称之为myItem

现在我希望someProxy->fooFunc()someProxy->fooProperty分别调用myItem.fooFunc()myItem.fooProperty

我遇到&#39;带着Object&#39; 类型的临时对象的地址警告。

3 个答案:

答案 0 :(得分:4)

如果您可以更改Object,则可以添加

class Object {
public:
    // other code
    const Object* operator -> () const { return this; }
    Object* operator -> () { return this; }
};

适用于您的Proxy

Object operator->() { return container[key]; }

所以,例如

myObj[42]->myFoo = ...

大致相当于

Proxy proxy = myObj[42];
Object obj = proxy.operator ->();
Object* pobj = obj.operator ->(); // so pobj = &obj;
pobj->myFoo = ...

答案 1 :(得分:1)

我发现你写的Proxy类作为一个例子有点令人困惑,所以我冒昧地改变了一点: 这是一个简单的例子:

//object with lots of members:
class my_obj
{
public:
    std::string     m_text;

    void foo()
    {
        std::cout << m_text << std::endl;
    }
    void bar(std::string t)
    {
        m_text = t;
    }
};
//proxy object
class proxy_class
{
private:
    friend class CustomContainer;
    my_obj* px;

    proxy_class(my_obj * obj_px)
        :px(obj_px)
    {
    }
    proxy_class() = delete;
    proxy_class(const proxy_class &) = delete;
    proxy_class& operator =(const proxy_class &) = delete;
public:

    my_obj* operator ->()
    {
        return px;
    }
};
//custom container that is the only one that can return proxy objects
class CustomContainer
{
public:
    std::map<std::size_t, my_obj> stuff;

    proxy_class     operator [](const std::size_t index)
    {
        return proxy_class(&stuff[index]);
    }
};

示例用法:

CustomContainer cc;
cc[0]->foo();
cc[0]->bar("hello world");
cc[0]->foo();

作为设计考虑因素,代理类应该在受控环境中创建,这样可以避免构造函数阻止错误使用。

CustomContainer必须仅返回proxy_class并引用my_obj,以便它可以使用任何内容,std::mapstd::vector

答案 2 :(得分:0)

经过几个小时的哄骗coliru,我有一个工作测试用例。

请参阅:https://codereview.stackexchange.com/questions/75237/c11-proxy-pattern-for-supporting-obidx-someobjmember-type-acc

非常感谢Jarod,为 - &gt;提供正确的语法和理解。过载。