临时使用 boost::python::handle?

Using boost::python::handle as temporary?

在自定义转换器中,我正在检查序列项是否为某种类型。到目前为止,我已经有了这段代码(简化)

namespace bp=boost::python;
/* ... */
static void* convertible(PyObject* seq_ptr){
    if(!PySequence_Check(seq_ptr)) return 0;
    for(int i=0; i<PySequence_Size(seq_ptr); i++) 
        if(!bp::extract<double>(PySequence_GetItem(seq_ptr,i)).check()) return 0;
    /* ... */
}
/* ... */

但这是内存泄漏,因为 PySequence_GetItem is returning a new reference。所以要么我可以在循环中做这样的事情:

PyObject* it=PySequence_GetItem(seq_ptr,i);
bool ok(bp::extract<double>(it).check();
Py_DECREF(it); // will delete the object which had been newly created
if(!ok) return 0;

但这很笨拙;我可以创建一个独立的函数来执行此操作,但那是我记得 bp::handle 实现引用计数机制的地方;所以这样的事情可能会做:

if(!bp::extract<double>(bp::handle<>(PySequence_GetItem(seq_ptr,i))).check()) return 0;

this page 提到不鼓励使用句柄作为临时句柄。为什么?在实际调用 .check() 之前可以销毁对象吗?还有其他优雅的写法吗?

对象在调用 .check() 之前不会被销毁,并且在发布的上下文中是安全的。

不使用临时变量的建议是由于未指定参数评估顺序和异常安全性。如果只有一种可以评估参数的顺序,例如在您的示例中,那么它是安全的。例如,考虑总是抛出异常的函数 bad()

f(boost::python::handle<>(PySequence_GetItem(...)), bad());

如果 bad()PySequence_GetItem(...)boost::python::handle<>(...) 之间求值,那么新引用将被泄漏,因为堆栈将在 boost::python::handle<> 的构造之前开始展开。另一方面,当使用非临时变量时,在 PySequence_GetItem()boost::python::handle<>() 之间没有机会抛出某些东西,因此在存在异常时以下内容是安全的:

boost::python::handle<> item_handle(PySequence_GetItem(...));
f(item_handle, bad());

考虑阅读 Herb Sutter 的 GotW #56: Exception-Safe Function Calls 了解更多详细信息。