|
Boost.PythonCalling Python Functions and Methods |
Boost.Python provides two families of function templates,
call
and call_method
, for
invoking Python functions and methods respectively. The interface for
calling a Python function object (or any Python callable object) looks
like:
call<ResultType>(callable_object, a1, a2... aN);Calling a method of a Python object is similarly easy:
call_method<ResultType>(self_object, "method-name", a1, a2... aN);
Arguments are converted to Python according to their type. By default,
the arguments a1
...aN
are copied into
new Python objects, but this behavior can be overridden by the use of
ptr()
and ref():
class X : boost::noncopyable { ... }; void apply(PyObject* callable, X& x) { // Invoke callable, passing a Python object which holds a reference to x boost::python::call<void>(callable, boost::ref(x)); }In the table below,
x
denotes the actual argument
object and cv
denotes an optional
cv-qualification: "const
",
"volatile
", or "const
volatile
".
Argument Type | Behavior |
---|---|
T cv& T cv
| The Python argument is created by the same means used
for the return value of a wrapped C++ function returning
T . When
T is a class type, that normally means
*x is copy-constructed into the new Python
object.
|
T*
| If x == 0 , the Python argument will
be None . Otherwise,
the Python argument is created by the same means used for the
return value of a wrapped C++ function returning
T . When
T is a class type, that normally means
*x is copy-constructed into the new Python
object.
|
boost::reference_wrapper<T>
| The Python argument contains a pointer to, rather than a
copy of, x.get() . Note: failure to ensure that no
Python code holds a reference to the resulting object beyond
the lifetime of *x.get() may result in a
crash!
|
pointer_wrapper<T>
| If x.get() == 0 , the Python
argument will be None .
Otherwise, the Python argument contains a pointer to, rather
than a copy of, *x.get() . Note: failure to ensure
that no Python code holds a reference to the resulting object
beyond the lifetime of *x.get() may result in
a crash!
|
call<ResultType>()
and
call_method<ResultType>()
return
ResultType
by exploiting all lvalue and rvalue
from_python
converters registered for ResultType and
returning a copy of the result. However, when
ResultType
is a pointer or reference type, Boost.Python
searches only for lvalue converters. To prevent dangling pointers and
references, an exception will be thrown if the Python result object
has only a single reference count.
a1
...aN
, a new Python object must be
created for each one; should the C++ object be copied into that Python
object, or should the Python object simply hold a reference/pointer to
the C++ object? In general, the latter approach is unsafe, since the
called function may store a reference to the Python object
somewhere. If the Python object is used after the C++ object is
destroyed, we'll crash Python.
In keeping with the philosophy that users on the Python side
shouldn't have to worry about crashing the interpreter, the default
behavior is to copy the C++ object, and to allow a non-copying
behavior only if the user writes boost::ref(a1)
instead of a1
directly. At least this way, the user doesn't get dangerous behavior
"by accident". It's also worth noting that the non-copying
("by-reference") behavior is in general only available for
class types, and will fail at runtime with a Python exception if used
otherwise[1].
However, pointer types present a problem: one approach is to refuse
to compile if any aN has pointer type: after all, a user can always pass
*aN
to pass "by-value" or ref(*aN)
to indicate a pass-by-reference behavior. However, this creates a
problem for the expected null pointer to
None
conversion: it's illegal to dereference a null
pointer value.
The compromise I've settled on is this:
ptr(aN)
if
aN
is a pointer and ref(aN)
otherwise. If
a null pointer is passed to ptr(aN)
, the corresponding
Python argument will be None
.
As for results, we have a similar problem: if ResultType
is allowed to be a pointer or reference type, the lifetime of the
object it refers to is probably being managed by a Python object. When
that Python object is destroyed, our pointer dangles. The problem is
particularly bad when the ResultType
is char const* - the
corresponding Python String object is typically uniquely-referenced,
meaning that the pointer dangles as soon as call<char
const*>(...)
returns.
The old Boost.Python v1 deals with this issue by refusing to compile
any uses of call<char const*>()
, but this goes both
too far and not far enough. It goes too far because there are cases
where the owning Python string object survives beyond the call (just
for instance, when it's the name of a Python class), and it goes not
far enough because we might just as well have the same problem with a
returned pointer or reference of any other type.
In Boost.Python v2 this is dealt with by:
call<U>(...)
when U
is a pointer
or reference type.
U
in
call<U>
, and they will be protected against dangles
at runtime, at least long enough to get out of the
call<U>(...)
invocation.
Revised 17 April, 2002
© Copyright Dave Abrahams 2002. All Rights Reserved.