在 C++11 中实现递归代理模式
Implementing Recursive Proxy pattern in C++11
假设我们有一些Foo
对象允许:
cout << myFoo[3];
myFoo[5] = "bar";
这需要一个代理设计模式(Scott Meyers在这里详细介绍)
但现在让我们假设每个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 {
:
这意味着我们不能再在 Object
中定义Proxy
,否则我们将得到一个"尝试从不完整类型基于"编译器错误。
所以让我们这样做。 并且我们还必须在可能的情况下修改构造函数以填充基类:
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
不能。
它似乎是第 22 条军规,我看不到任何干净的解决方案。
有吗?
编辑:作为对建议有缺陷设计的评论的回应,请记住,Object
是一个围绕指针的轻量级包装器。它只有一个PyObject*
数据成员。
编辑:我正在使用的原始代码可以在这里找到
你的前提似乎有缺陷。根据定义,Proxy
不是Object
;如果是这样,那么你一开始就不会称它为Proxy
。然后你可以在没有代理的情况下解决问题,就像std::map
这样的标准数据类型解决问题一样:只需在必要时operator[]
返回对新创建的Object
的引用。
您正在寻找类似于 std::vector<bool>
的代理模式:operator[]
返回一个带有operator=
和隐式转换为非代理Object
的Proxy
(适用于您真正想要使用该值而不是赋值的情况)。
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}; }
};
我最终解决了这个问题。
诀窍是简单地将类用作自己的代理。
因此,最初代理对象提供转换以区分左值和右值访问的地方,我只是将这些转换移回我的原始对象类:
mutable bool m_resolve_me{false};
PyObject* m_container{nullptr};
PyObject* m_key{nullptr};
public:
// Rvalue (e.g. x = ob[42];)
const Object operator[] (const Object& key) const {
return Object{ PyObject_GetItem( p, key.p ) };
}
// Don't know yet
Object operator[] (const Object& key) {
return Object{ *this, key };
}
// notice we set the m_resolve_me flag
// as we don't yet know L/Rvalue-ness
Object( const Object& c, const Object& k )
: m_container{c.p}, m_key{k.p}, m_resolve_me{true}
{
// for all but lvalue access (ob[idx]=...), ob[idx] will be valid
p = PyObject_GetItem( m_container, m_key );
if( p == nullptr ) {
// ... However in the case of lvalue access,
// PyObject_GetItem will set Python's error indicator
// so we must flush that error, as it was expected!
PyErr_Clear();
p = charge(Py_None);
}
// ^ either way, p ends up charged
}
public:
// this will attempt to convert ANY rhs to Object, which takes advantage of ALL the above constructor overrides
Object& operator=( const Object& rhs )
{
/*
1) normal situation
2) this object is m_resolve_me, and we are assigning
a normal object to it
3) this object is m_resolve_me, and we are assigning
a m_resolve_me object to it
4) this object is normal, and we are assigning a m_resolve_me object to it
1) we need to charge p
2) same
3) same
4) same
The only important thing is: we have to be neutral to rhs.p
That means we have to charge it, as we will be
subsequently neutralising it in the destructor
*/
if( &rhs != this )
*this = charge(rhs.p);
return *this;
}
// (Always) assume charged pointer
Object& operator=( PyObject* pyob )
{
if( m_resolve_me ) {
PyObject_SetItem( m_container, m_key, pyob );
m_resolve_me = false;
}
set_ptr( pyob );
return *this;
}
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 代理对象的常量正确性
- 为什么在保护模式下继承升级不起作用
- 如何在全屏模式下(在OpenGL中)使背景透明
- 为什么使用__LINE_的代码在发布模式下在MSVC下编译,而不是在调试模式下
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 有什么好的方法可以让系统调用代理允许在单元测试中进行模拟
- 此模式的C++RegEx
- avrogencpp能为模式中的每种类型生成单独的头文件吗
- 使用可变模板的Broadcaster/Listener模式
- c++方法参数只能在linux的发布模式下自行更改
- 资源管理设计模式
- 使用 mod_gsoap 部署服务时,如何在 Gsoap 中更改 soap 上下文的模式?
- C++ 无法在字符数组中使用 for 循环打印字母模式
- 哪种设计模式仅出于代理目的而组成的对象与之相关
- C++ 中的代理模式问题
- 在 C++11 中实现递归代理模式
- 代理是否适合创建演示模式?
- 具有代理设计模式的预处理器
- 设计模式- c++代理类