0

In a custom converter, I am checking whether a sequence item is some type. So far I've had this code (simplified)

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;
    /* ... */
}
/* ... */

but this is leaking memory, since PySequence_GetItem is returning a new reference. So either I can do something like this in the loop:

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;

but that is quite clumsy; I could make a stand-alone function doing that, but that is where I recalled bp::handle implementing the ref-counting machinery; so something like this might do:

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

but this page mentions using handles as temporaries is discouraged. Why? Can the object be destroyed before .check() is actually called? Is there some other elegant way to write this?

1 Answer 1

1

The object will not be destroyed before the .check() is called and is safe in the posted context.

The recommendation to not use temporaries is due to the unspecified order of evaluation of the arguments and exception safety. If there is only one order in which arguments can be evaluated, such as in your example, then it is safe. For instance, consider function bad() which always throws an exception:

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

If bad() gets evaluated between PySequence_GetItem(...) and boost::python::handle<>(...), then the new reference will be leaked as the stack will begin to unwind before the construction of boost::python::handle<>. On the other hand, when a non-temporary is used, there is no chance for something to throw between PySequence_GetItem() and boost::python::handle<>(), so the following is safe in the presence of exceptions:

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

Consider reading Herb Sutter's GotW #56: Exception-Safe Function Calls for more details.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.