重载 - >运算符通过代理转发成员访问

Overload -> operator to forward member-access through Proxy

本文关键字:代理 转发 成员 访问 运算符 gt 重载      更新时间:2023-10-16

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

我正在尝试通过代理模式(此处)允许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(); }

即手动通过Proxy传递Object的方法,这是丑陋的。

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

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&"遇到"获取对象类型的临时对象的地址"警告。

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

我遇到了"获取对象类型的临时对象的地址"警告。

如果可以更改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 = ...

我发现您作为示例编写的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只需要返回带有对my_obj引用的proxy_class,这样它就可以使用任何东西,std::mapstd::vector

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

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

非常感谢 Jarod,为 -> 重载提供了正确的语法和理解。