Files
pybindgen/pybindgen/cppclass_typehandlers.py
T

982 lines
50 KiB
Python

import warnings
from typehandlers.base import ForwardWrapperBase, ReverseWrapperBase, \
Parameter, ReturnValue, TypeConfigurationError, NotSupportedError
import cppclass
###
### ------------ C++ class parameter type handlers ------------
###
class CppClassParameterBase(Parameter):
"Base class for all C++ Class parameter handlers"
CTYPES = []
cpp_class = cppclass.CppClass('dummy') # CppClass instance
DIRECTIONS = [Parameter.DIRECTION_IN]
def __init__(self, ctype, name, direction=Parameter.DIRECTION_IN, is_const=False, default_value=None):
"""
ctype -- C type, normally 'MyClass*'
name -- parameter name
"""
if ctype == self.cpp_class.name:
ctype = self.cpp_class.full_name
super(CppClassParameterBase, self).__init__(
ctype, name, direction, is_const, default_value)
## name of the PyFoo * variable used in parameter parsing
self.py_name = None
## it True, this parameter is 'fake', and instead of being
## passed a parameter from python it is assumed to be the
## 'self' parameter of a method wrapper
self.take_value_from_python_self = False
class CppClassReturnValueBase(ReturnValue):
"Class return handlers -- base class"
CTYPES = []
cpp_class = cppclass.CppClass('dummy') # CppClass instance
def __init__(self, ctype, is_const=False):
super(CppClassReturnValueBase, self).__init__(ctype, is_const=is_const)
## name of the PyFoo * variable used in return value building
self.py_name = None
class CppClassParameter(CppClassParameterBase):
"Class handlers"
CTYPES = []
cpp_class = cppclass.CppClass('dummy') # CppClass instance
DIRECTIONS = [Parameter.DIRECTION_IN]
def convert_python_to_c(self, wrapper):
"parses python args to get C++ value"
assert isinstance(wrapper, ForwardWrapperBase)
assert isinstance(self.cpp_class, cppclass.CppClass)
if self.take_value_from_python_self:
self.py_name = 'self'
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
implicit_conversion_sources = self.cpp_class.get_all_implicit_conversions()
if not implicit_conversion_sources:
if self.default_value is not None:
self.cpp_class.get_construct_name() # raises an exception if the class cannot be constructed
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name, 'NULL')
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=True)
wrapper.call_params.append(
'(%s ? (*((%s *) %s)->obj) : %s)' % (self.py_name, self.cpp_class.pystruct, self.py_name, self.default_value))
else:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
if self.default_value is None:
self.py_name = wrapper.declarations.declare_variable(
'PyObject*', self.name)
tmp_value_variable = wrapper.declarations.declare_variable(
self.cpp_class.full_name, self.name)
wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name)
else:
self.py_name = wrapper.declarations.declare_variable(
'PyObject*', self.name, 'NULL')
tmp_value_variable = wrapper.declarations.declare_variable(
self.cpp_class.full_name, self.name)
wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name, optional=True)
if self.default_value is None:
wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, self.cpp_class.pytypestruct,
tmp_value_variable,
self.cpp_class.pystruct, self.py_name))
else:
wrapper.before_call.write_code(
"if (%s == NULL) {\n"
" %s = %s;" %
(self.py_name, tmp_value_variable, self.default_value))
wrapper.before_call.write_code(
"} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, self.cpp_class.pytypestruct,
tmp_value_variable,
self.cpp_class.pystruct, self.py_name))
for conversion_source in implicit_conversion_sources:
wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, conversion_source.pytypestruct,
tmp_value_variable,
conversion_source.pystruct, self.py_name))
wrapper.before_call.write_code("} else {\n")
wrapper.before_call.indent()
possible_type_names = ", ".join([cls.name for cls in [self.cpp_class] + implicit_conversion_sources])
wrapper.before_call.write_code("PyErr_Format(PyExc_TypeError, \"parameter must an instance of one of the types (%s), not %%s\", %s->ob_type->tp_name);" % (possible_type_names, self.py_name))
wrapper.before_call.write_error_return()
wrapper.before_call.unindent()
wrapper.before_call.write_code("}")
wrapper.call_params.append(tmp_value_variable)
def convert_c_to_python(self, wrapper):
'''Write some code before calling the Python method.'''
assert isinstance(wrapper, ReverseWrapperBase)
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name)
if self.cpp_class.allow_subclassing:
new_func = 'PyObject_GC_New'
else:
new_func = 'PyObject_New'
wrapper.before_call.write_code(
"%s = %s(%s, %s);" %
(self.py_name, new_func, self.cpp_class.pystruct, '&'+self.cpp_class.pytypestruct))
if self.cpp_class.allow_subclassing:
wrapper.before_call.write_code(
"%s->inst_dict = NULL;" % (self.py_name,))
self.cpp_class.write_create_instance(wrapper.before_call,
"%s->obj" % self.py_name,
self.value)
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, self.py_name,
"%s->obj" % self.py_name)
wrapper.build_params.add_parameter("N", [self.py_name])
class CppClassRefParameter(CppClassParameterBase):
"Class& handlers"
CTYPES = []
cpp_class = cppclass.CppClass('dummy') # CppClass instance
DIRECTIONS = [Parameter.DIRECTION_IN,
Parameter.DIRECTION_OUT,
Parameter.DIRECTION_INOUT]
def __init__(self, ctype, name, direction=Parameter.DIRECTION_IN, is_const=False,
default_value=None, default_value_type=None):
"""
@param ctype: C type, normally 'MyClass*'
@param name: parameter name
"""
if ctype == self.cpp_class.name:
ctype = self.cpp_class.full_name
super(CppClassRefParameter, self).__init__(
ctype, name, direction, is_const, default_value)
self.default_value_type = default_value_type
def convert_python_to_c(self, wrapper):
"parses python args to get C++ value"
assert isinstance(wrapper, ForwardWrapperBase)
assert isinstance(self.cpp_class, cppclass.CppClass)
if self.direction == Parameter.DIRECTION_IN:
if self.take_value_from_python_self:
self.py_name = 'self'
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
implicit_conversion_sources = self.cpp_class.get_all_implicit_conversions()
if not (implicit_conversion_sources and self.type_traits.target_is_const):
if self.default_value is not None:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name, 'NULL')
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=True)
if self.default_value_type is not None:
default_value_name = wrapper.declarations.declare_variable(
self.default_value_type, "%s_default" % self.name,
self.default_value)
wrapper.call_params.append(
'(%s ? (*((%s *) %s)->obj) : %s)' % (self.py_name, self.cpp_class.pystruct,
self.py_name, default_value_name))
else:
self.cpp_class.get_construct_name() # raises an exception if the class cannot be constructed
wrapper.call_params.append(
'(%s ? (*((%s *) %s)->obj) : %s)' % (self.py_name, self.cpp_class.pystruct,
self.py_name, self.default_value))
else:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
if self.default_value is not None:
warnings.warn("with implicit conversions, default value "
"in C++ class reference parameters is ignored.")
self.py_name = wrapper.declarations.declare_variable(
'PyObject*', self.name)
tmp_value_variable = wrapper.declarations.declare_variable(
self.cpp_class.full_name, self.name)
wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name)
wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, self.cpp_class.pytypestruct,
tmp_value_variable,
self.cpp_class.pystruct, self.py_name))
for conversion_source in implicit_conversion_sources:
wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, conversion_source.pytypestruct,
tmp_value_variable,
conversion_source.pystruct, self.py_name))
wrapper.before_call.write_code("} else {\n")
wrapper.before_call.indent()
possible_type_names = ", ".join([cls.name for cls in [self.cpp_class] + implicit_conversion_sources])
wrapper.before_call.write_code("PyErr_Format(PyExc_TypeError, \"parameter must an instance of one of the types (%s), not %%s\", %s->ob_type->tp_name);" % (possible_type_names, self.py_name))
wrapper.before_call.write_error_return()
wrapper.before_call.unindent()
wrapper.before_call.write_code("}")
wrapper.call_params.append(tmp_value_variable)
elif self.direction == Parameter.DIRECTION_OUT:
assert not self.take_value_from_python_self
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
if self.cpp_class.allow_subclassing:
new_func = 'PyObject_GC_New'
else:
new_func = 'PyObject_New'
wrapper.before_call.write_code(
"%s = %s(%s, %s);" %
(self.py_name, new_func, self.cpp_class.pystruct,
'&'+self.cpp_class.pytypestruct))
if self.cpp_class.allow_subclassing:
wrapper.after_call.write_code(
"%s->inst_dict = NULL;" % (self.py_name,))
self.cpp_class.write_create_instance(wrapper.before_call,
"%s->obj" % self.py_name,
'')
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, self.py_name,
"%s->obj" % self.py_name)
wrapper.call_params.append('*%s->obj' % (self.py_name,))
wrapper.build_params.add_parameter("N", [self.py_name])
## well, personally I think inout here doesn't make much sense
## (it's just plain confusing), but might as well support it..
## C++ class reference inout parameters allow "inplace"
## modifications, i.e. the object is not explicitly returned
## but is instead modified by the callee.
elif self.direction == Parameter.DIRECTION_INOUT:
assert not self.take_value_from_python_self
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append(
'*%s->obj' % (self.py_name))
def convert_c_to_python(self, wrapper):
'''Write some code before calling the Python method.'''
assert isinstance(wrapper, ReverseWrapperBase)
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name)
if self.cpp_class.allow_subclassing:
new_func = 'PyObject_GC_New'
else:
new_func = 'PyObject_New'
wrapper.before_call.write_code(
"%s = %s(%s, %s);" %
(self.py_name, new_func, self.cpp_class.pystruct, '&'+self.cpp_class.pytypestruct))
if self.cpp_class.allow_subclassing:
wrapper.before_call.write_code(
"%s->inst_dict = NULL;" % (self.py_name,))
if self.direction == Parameter.DIRECTION_IN:
self.cpp_class.write_create_instance(wrapper.before_call,
"%s->obj" % self.py_name,
self.value)
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, self.py_name,
"%s->obj" % self.py_name)
wrapper.build_params.add_parameter("N", [self.py_name])
else:
## out/inout case:
## the callee receives a "temporary wrapper", which loses
## the ->obj pointer after the python call; this is so
## that the python code directly manipulates the object
## received as parameter, instead of a copy.
if self.type_traits.target_is_const:
value = "(%s*) (&(%s))" % (self.cpp_class.full_name, self.value)
else:
value = "&(%s)" % self.value
wrapper.before_call.write_code(
"%s->obj = %s;" % (self.py_name, value))
wrapper.build_params.add_parameter("O", [self.py_name])
wrapper.before_call.add_cleanup_code("Py_DECREF(%s);" % self.py_name)
if self.cpp_class.has_copy_constructor:
## if after the call we notice the callee kept a reference
## to the pyobject, we then swap pywrapper->obj for a copy
## of the original object. Else the ->obj pointer is
## simply erased (we never owned this object in the first
## place).
wrapper.after_call.write_code(
"if (%s->ob_refcnt == 1)\n"
" %s->obj = NULL;\n"
"else{\n" % (self.py_name, self.py_name))
wrapper.after_call.indent()
self.cpp_class.write_create_instance(wrapper.after_call,
"%s->obj" % self.py_name,
self.value)
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.after_call, self.py_name,
"%s->obj" % self.py_name)
wrapper.after_call.unindent()
wrapper.after_call.write_code('}')
else:
## it's not safe for the python wrapper to keep a
## pointer to the object anymore; just set it to NULL.
wrapper.after_call.write_code("%s->obj = NULL;" % (self.py_name,))
class CppClassReturnValue(CppClassReturnValueBase):
"Class return handlers"
CTYPES = []
cpp_class = cppclass.CppClass('dummy') # CppClass instance
REQUIRES_ASSIGNMENT_CONSTRUCTOR = True
def __init__(self, ctype, is_const=False):
"""override to fix the ctype parameter with namespace information"""
if ctype == self.cpp_class.name:
ctype = self.cpp_class.full_name
super(CppClassReturnValue, self).__init__(ctype, is_const=is_const)
def get_c_error_return(self): # only used in reverse wrappers
"""See ReturnValue.get_c_error_return"""
return "return %s();" % (self.cpp_class.full_name,)
def convert_c_to_python(self, wrapper):
"""see ReturnValue.convert_c_to_python"""
py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name)
self.py_name = py_name
if self.cpp_class.allow_subclassing:
new_func = 'PyObject_GC_New'
else:
new_func = 'PyObject_New'
wrapper.after_call.write_code(
"%s = %s(%s, %s);" %
(py_name, new_func, self.cpp_class.pystruct, '&'+self.cpp_class.pytypestruct))
if self.cpp_class.allow_subclassing:
wrapper.after_call.write_code(
"%s->inst_dict = NULL;" % (py_name,))
self.cpp_class.write_create_instance(wrapper.after_call,
"%s->obj" % py_name,
self.value)
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.after_call, py_name,
"%s->obj" % py_name)
wrapper.build_params.add_parameter("N", [py_name], prepend=True)
def convert_python_to_c(self, wrapper):
"""see ReturnValue.convert_python_to_c"""
name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', "tmp_%s" % self.cpp_class.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+name])
if self.REQUIRES_ASSIGNMENT_CONSTRUCTOR:
wrapper.after_call.write_code('%s %s = *%s->obj;' %
(self.cpp_class.full_name, self.value, name))
else:
wrapper.after_call.write_code('%s = *%s->obj;' % (self.value, name))
class CppClassPtrParameter(CppClassParameterBase):
"Class* handlers"
CTYPES = []
cpp_class = cppclass.CppClass('dummy') # CppClass instance
DIRECTIONS = [Parameter.DIRECTION_IN]
SUPPORTS_TRANSFORMATIONS = True
def __init__(self, ctype, name, transfer_ownership=None, custodian=None, is_const=False,
null_ok=False, default_value=None):
"""
@param ctype: C type, normally 'MyClass*'
@param name: parameter name
@param transfer_ownership: this parameter transfer the ownership of
the pointed-to object to the called
function; should be omitted if custodian
is given.
@param custodian: the object (custodian) that is responsible for
managing the life cycle of the parameter.
Possible values are:
- None: no object is custodian;
- -1: the return value object;
- 0: the instance of the method in which
the ReturnValue is being used will become the
custodian;
- integer > 0: parameter number, starting at 1
(i.e. not counting the self/this parameter),
whose object will be used as custodian.
@param is_const: if true, the parameter has a const attached to the leftmost
@param null_ok: if true, None is accepted and mapped into a C NULL pointer
@param default_value: default parameter value (as C expression
string); probably, the only default value that makes sense
here is probably 'NULL'.
@note: only arguments which are instances of C++ classes
wrapped by PyBindGen can be used as custodians.
"""
if ctype == self.cpp_class.name:
ctype = self.cpp_class.full_name
super(CppClassPtrParameter, self).__init__(
ctype, name, direction=Parameter.DIRECTION_IN, is_const=is_const)
if custodian is None:
if transfer_ownership is None:
if self.type_traits.target_is_const:
transfer_ownership = False
else:
raise TypeConfigurationError("transfer_ownership parameter missing")
self.transfer_ownership = transfer_ownership
else:
if transfer_ownership is not None:
raise TypeConfigurationError("the transfer_ownership parameter should "
"not be given when there is a custodian")
self.transfer_ownership = False
self.custodian = custodian
self.null_ok = null_ok
self.default_value = default_value
def convert_python_to_c(self, wrapper):
"parses python args to get C++ value"
assert isinstance(wrapper, ForwardWrapperBase)
assert isinstance(self.cpp_class, cppclass.CppClass)
if self.take_value_from_python_self:
self.py_name = 'self'
value_ptr = 'self->obj'
else:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name,
initializer=(self.default_value and 'NULL' or None))
value_ptr = wrapper.declarations.declare_variable("%s*" % self.cpp_class.full_name,
"%s_ptr" % self.name)
if self.null_ok:
num = wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name, optional=bool(self.default_value))
wrapper.before_call.write_error_check(
"%s && ((PyObject *) %s != Py_None) && !PyObject_IsInstance((PyObject *) %s, (PyObject *) &%s)"
% (self.py_name, self.py_name, self.py_name, self.cpp_class.pytypestruct),
'PyErr_SetString(PyExc_TypeError, "Parameter %i must be %s");' % (num, self.cpp_class.name))
wrapper.before_call.write_code("if (%(PYNAME)s) {\n"
" if ((PyObject *) %(PYNAME)s == Py_None)\n"
" %(VALUE)s = NULL;\n"
" else\n"
" %(VALUE)s = %(PYNAME)s->obj;\n"
"} else {\n"
" %(VALUE)s = NULL;\n"
"}" % dict(PYNAME=self.py_name, VALUE=value_ptr))
else:
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=bool(self.default_value))
wrapper.before_call.write_code("%s = (%s ? %s->obj : NULL);" % (value_ptr, self.py_name, self.py_name))
value = self.transformation.transform(self, wrapper.declarations, wrapper.before_call, value_ptr)
wrapper.call_params.append(value)
if self.transfer_ownership:
if not isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
# if we transfer ownership, in the end we no longer own the object, so clear our pointer
wrapper.after_call.write_code('if (%s)\n %s->obj = NULL;' % (self.py_name, self.py_name,))
else:
wrapper.before_call.write_code("if (%s) {" % self.py_name)
wrapper.before_call.indent()
self.cpp_class.memory_policy.write_incref(wrapper.before_call, "%s->obj" % self.py_name)
wrapper.before_call.unindent()
wrapper.before_call.write_code("}")
def convert_c_to_python(self, wrapper):
"""foo"""
## Value transformations
value = self.transformation.untransform(
self, wrapper.declarations, wrapper.after_call, self.value)
## declare wrapper variable
py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name)
self.py_name = py_name
def write_create_new_wrapper():
"""Code path that creates a new wrapper for the parameter"""
## Find out what Python wrapper to use, in case
## automatic_type_narrowing is active and we are not forced to
## make a copy of the object
if (self.cpp_class.automatic_type_narrowing
and (self.transfer_ownership or isinstance(self.cpp_class.memory_policy,
cppclass.ReferenceCountingPolicy))):
typeid_map_name = self.cpp_class.get_type_narrowing_root().typeid_map_name
wrapper_type = wrapper.declarations.declare_variable(
'PyTypeObject*', 'wrapper_type', '0')
wrapper.before_call.write_code(
'%s = %s.lookup_wrapper(typeid(*%s), &%s);'
% (wrapper_type, typeid_map_name, value, self.cpp_class.pytypestruct))
else:
wrapper_type = '&'+self.cpp_class.pytypestruct
## Create the Python wrapper object
if self.cpp_class.allow_subclassing:
new_func = 'PyObject_GC_New'
else:
new_func = 'PyObject_New'
wrapper.before_call.write_code(
"%s = %s(%s, %s);" %
(py_name, new_func, self.cpp_class.pystruct, wrapper_type))
self.py_name = py_name
if self.cpp_class.allow_subclassing:
wrapper.before_call.write_code(
"%s->inst_dict = NULL;" % (py_name,))
## Assign the C++ value to the Python wrapper
if self.transfer_ownership:
wrapper.before_call.write_code("%s->obj = %s;" % (py_name, value))
else:
if not isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
## The PyObject gets a temporary pointer to the
## original value; the pointer is converted to a
## copy in case the callee retains a reference to
## the object after the call.
if self.direction == Parameter.DIRECTION_IN:
self.cpp_class.write_create_instance(wrapper.before_call,
"%s->obj" % self.py_name,
'*'+self.value)
else:
## out/inout case:
## the callee receives a "temporary wrapper", which loses
## the ->obj pointer after the python call; this is so
## that the python code directly manipulates the object
## received as parameter, instead of a copy.
if self.type_traits.target_is_const:
unconst_value = "(%s*) (%s)" % (self.cpp_class.full_name, value)
else:
unconst_value = value
wrapper.before_call.write_code(
"%s->obj = %s;" % (self.py_name, unconst_value))
wrapper.build_params.add_parameter("O", [self.py_name])
wrapper.before_call.add_cleanup_code("Py_DECREF(%s);" % self.py_name)
## if after the call we notice the callee kept a reference
## to the pyobject, we then swap pywrapper->obj for a copy
## of the original object. Else the ->obj pointer is
## simply erased (we never owned this object in the first
## place).
wrapper.after_call.write_code(
"if (%s->ob_refcnt == 1)\n"
" %s->obj = NULL;\n"
"else {\n" % (self.py_name, self.py_name))
wrapper.after_call.indent()
self.cpp_class.write_create_instance(wrapper.after_call,
"%s->obj" % self.py_name,
'*'+value)
wrapper.after_call.unindent()
wrapper.after_call.write_code('}')
else:
## The PyObject gets a new reference to the same obj
self.cpp_class.memory_policy.write_incref(wrapper.before_call, value)
if self.type_traits.target_is_const:
wrapper.before_call.write_code("%s->obj = (%s*) (%s);" %
(py_name, self.cpp_class.full_name, value))
else:
wrapper.before_call.write_code("%s->obj = %s;" % (py_name, value))
## closes def write_create_new_wrapper():
if self.cpp_class.helper_class is None:
try:
self.cpp_class.wrapper_registry.write_lookup_wrapper(
wrapper.before_call, self.cpp_class.pystruct, py_name, value)
except NotSupportedError:
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, py_name,
"%s->obj" % py_name)
else:
wrapper.before_call.write_code("if (%s == NULL)\n{" % py_name)
wrapper.before_call.indent()
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, py_name,
"%s->obj" % py_name)
wrapper.before_call.unindent()
wrapper.before_call.write_code('}')
wrapper.build_params.add_parameter("N", [py_name])
else:
wrapper.before_call.write_code("if (typeid(*(%s)) == typeid(%s))\n{"
% (value, self.cpp_class.helper_class.name))
wrapper.before_call.indent()
if self.type_traits.target_is_const:
wrapper.before_call.write_code(
"%s = (%s*) (((%s*) ((%s*) %s))->m_pyself);"
% (py_name, self.cpp_class.pystruct,
self.cpp_class.helper_class.name, self.cpp_class.full_name, value))
wrapper.before_call.write_code("%s->obj = (%s*) (%s);" %
(py_name, self.cpp_class.full_name, value))
else:
wrapper.before_call.write_code(
"%s = (%s*) (((%s*) %s)->m_pyself);"
% (py_name, self.cpp_class.pystruct,
self.cpp_class.helper_class.name, value))
wrapper.before_call.write_code("%s->obj = %s;" % (py_name, value))
wrapper.before_call.write_code("Py_INCREF(%s);" % py_name)
wrapper.before_call.unindent()
wrapper.before_call.write_code("} else {")
wrapper.before_call.indent()
try:
self.cpp_class.wrapper_registry.write_lookup_wrapper(
wrapper.before_call, self.cpp_class.pystruct, py_name, value)
except NotSupportedError:
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(
wrapper.before_call, py_name, "%s->obj" % py_name)
else:
wrapper.before_call.write_code("if (%s == NULL)\n{" % py_name)
wrapper.before_call.indent()
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, py_name,
"%s->obj" % py_name)
wrapper.before_call.unindent()
wrapper.before_call.write_code('}') # closes if (%s == NULL)
wrapper.before_call.unindent()
wrapper.before_call.write_code("}") # closes if (typeid(*(%s)) == typeid(%s))\n{
wrapper.build_params.add_parameter("N", [py_name])
def _add_ward(wrapper, custodian, ward):
wards = wrapper.declarations.declare_variable(
'PyObject*', 'wards')
wrapper.after_call.write_code(
"%(wards)s = PyObject_GetAttrString(%(custodian)s, (char *) \"__wards__\");"
% vars())
wrapper.after_call.write_code(
"if (%(wards)s == NULL) {\n"
" PyErr_Clear();\n"
" %(wards)s = PyList_New(0);\n"
" PyObject_SetAttrString(%(custodian)s, (char *) \"__wards__\", %(wards)s);\n"
"}" % vars())
wrapper.after_call.write_code("PyList_Append(%s, %s);" % (wards, ward))
wrapper.after_call.add_cleanup_code("Py_DECREF(%s);" % wards)
class CppClassPtrReturnValue(CppClassReturnValueBase):
"Class* return handler"
CTYPES = []
SUPPORTS_TRANSFORMATIONS = True
cpp_class = cppclass.CppClass('dummy') # CppClass instance
def __init__(self, ctype, caller_owns_return=None, custodian=None, is_const=False):
"""
@param ctype: C type, normally 'MyClass*'
@param caller_owns_return: if true, ownership of the object pointer
is transferred to the caller; should be
omitted when custodian is given.
@param custodian: the object (custodian) that becomes responsible for
managing the life cycle of the return value.
Possible values are:
- None: no object is custodian;
- 0: the instance of the method in which
the ReturnValue is being used will become the
custodian;
- integer > 0: parameter number, starting at 1
(i.e. not counting the self/this parameter),
whose object will be used as custodian.
@note: only arguments which are instances of C++ classes
wrapped by PyBindGen can be used as custodians.
"""
if ctype == self.cpp_class.name:
ctype = self.cpp_class.full_name
super(CppClassPtrReturnValue, self).__init__(ctype, is_const=is_const)
if custodian is None:
if caller_owns_return is None:
if self.type_traits.target_is_const:
caller_owns_return = False
else:
raise TypeConfigurationError("caller_owns_return not given")
else:
if caller_owns_return is not None:
raise TypeConfigurationError("caller_owns_return should not given together with custodian")
self.custodian = custodian
if custodian is None:
self.caller_owns_return = caller_owns_return
else:
assert caller_owns_return is None
self.caller_owns_return = True
def get_c_error_return(self): # only used in reverse wrappers
"""See ReturnValue.get_c_error_return"""
return "return NULL;"
def convert_c_to_python(self, wrapper):
"""See ReturnValue.convert_c_to_python"""
## Value transformations
value = self.transformation.untransform(
self, wrapper.declarations, wrapper.after_call, self.value)
wrapper.after_call.write_code("if (!(%s)) {\n"
" Py_INCREF(Py_None);\n"
" return Py_None;\n"
"}" % value)
## declare wrapper variable
py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name)
self.py_name = py_name
def write_create_new_wrapper():
"""Code path that creates a new wrapper for the returned object"""
## Find out what Python wrapper to use, in case
## automatic_type_narrowing is active and we are not forced to
## make a copy of the object
if (self.cpp_class.automatic_type_narrowing
and (self.caller_owns_return or isinstance(self.cpp_class.memory_policy,
cppclass.ReferenceCountingPolicy))):
typeid_map_name = self.cpp_class.get_type_narrowing_root().typeid_map_name
wrapper_type = wrapper.declarations.declare_variable(
'PyTypeObject*', 'wrapper_type', '0')
wrapper.after_call.write_code(
'%s = %s.lookup_wrapper(typeid(*%s), &%s);'
% (wrapper_type, typeid_map_name, value, self.cpp_class.pytypestruct))
else:
wrapper_type = '&'+self.cpp_class.pytypestruct
## Create the Python wrapper object
if self.cpp_class.allow_subclassing:
new_func = 'PyObject_GC_New'
else:
new_func = 'PyObject_New'
wrapper.after_call.write_code(
"%s = %s(%s, %s);" %
(py_name, new_func, self.cpp_class.pystruct, wrapper_type))
self.py_name = py_name
if self.cpp_class.allow_subclassing:
wrapper.after_call.write_code(
"%s->inst_dict = NULL;" % (py_name,))
## Assign the C++ value to the Python wrapper
if self.caller_owns_return:
wrapper.after_call.write_code("%s->obj = %s;" % (py_name, value))
else:
if not isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
## The PyObject creates its own copy
self.cpp_class.write_create_instance(wrapper.after_call,
"%s->obj" % py_name,
'*'+value)
else:
## The PyObject gets a new reference to the same obj
self.cpp_class.memory_policy.write_incref(wrapper.after_call, value)
if self.type_traits.target_is_const:
wrapper.after_call.write_code("%s->obj = (%s*) (%s);" %
(py_name, self.cpp_class.full_name, value))
else:
wrapper.after_call.write_code("%s->obj = %s;" % (py_name, value))
## closes def write_create_new_wrapper():
if self.cpp_class.helper_class is None:
try:
self.cpp_class.wrapper_registry.write_lookup_wrapper(
wrapper.after_call, self.cpp_class.pystruct, py_name, value)
except NotSupportedError:
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(
wrapper.after_call, py_name, "%s->obj" % py_name)
else:
wrapper.after_call.write_code("if (%s == NULL) {" % py_name)
wrapper.after_call.indent()
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(
wrapper.after_call, py_name, "%s->obj" % py_name)
wrapper.after_call.unindent()
# If we are already referencing the existing python wrapper,
# we do not need a reference to the C++ object as well.
if self.caller_owns_return and \
isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
wrapper.after_call.write_code("} else {")
wrapper.after_call.indent()
self.cpp_class.memory_policy.write_decref(wrapper.after_call, value)
wrapper.after_call.unindent()
wrapper.after_call.write_code("}")
else:
wrapper.after_call.write_code("}")
else:
wrapper.after_call.write_code("if (typeid(*(%s)) == typeid(%s))\n{"
% (value, self.cpp_class.helper_class.name))
wrapper.after_call.indent()
if self.type_traits.target_is_const:
const_cast_value = "const_cast<%s *>(%s) " % (self.cpp_class.full_name, value)
else:
const_cast_value = value
wrapper.after_call.write_code(
"%s = reinterpret_cast< %s* >(reinterpret_cast< %s* >(%s)->m_pyself);"
% (py_name, self.cpp_class.pystruct,
self.cpp_class.helper_class.name, const_cast_value))
wrapper.after_call.write_code("%s->obj = %s;" % (py_name, const_cast_value))
# We are already referencing the existing python wrapper,
# so we do not need a reference to the C++ object as well.
if self.caller_owns_return and \
isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
self.cpp_class.memory_policy.write_decref(wrapper.after_call, value)
wrapper.after_call.write_code("Py_INCREF(%s);" % py_name)
wrapper.after_call.unindent()
wrapper.after_call.write_code("} else {") # if (typeid(*(%s)) == typeid(%s)) { ...
wrapper.after_call.indent()
# creater new wrapper or reference existing one
try:
self.cpp_class.wrapper_registry.write_lookup_wrapper(
wrapper.after_call, self.cpp_class.pystruct, py_name, value)
except NotSupportedError:
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(
wrapper.after_call, py_name, "%s->obj" % py_name)
else:
wrapper.after_call.write_code("if (%s == NULL) {" % py_name)
wrapper.after_call.indent()
write_create_new_wrapper()
self.cpp_class.wrapper_registry.write_register_new_wrapper(
wrapper.after_call, py_name, "%s->obj" % py_name)
wrapper.after_call.unindent()
if self.caller_owns_return and \
isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
wrapper.after_call.write_code("} else {")
wrapper.after_call.indent()
# If we are already referencing the existing python wrapper,
# we do not need a reference to the C++ object as well.
self.cpp_class.memory_policy.write_decref(wrapper.after_call, value)
wrapper.after_call.unindent()
wrapper.after_call.write_code("}")
else:
wrapper.after_call.write_code("}")
wrapper.after_call.unindent()
wrapper.after_call.write_code("}") # closes: if (typeid(*(%s)) == typeid(%s)) { ... } else { ...
wrapper.build_params.add_parameter("N", [py_name], prepend=True)
if self.custodian is None:
pass
elif self.custodian == 0:
assert wrapper.class_.allow_subclassing
_add_ward(wrapper, '((PyObject *) self)', "((PyObject *) %s)" % py_name)
else:
assert self.custodian > 0
param = wrapper.parameters[self.custodian - 1]
assert param.cpp_class.allow_subclassing
assert isinstance(param, CppClassParameterBase)
_add_ward(wrapper, "((PyObject *) %s)" % param.py_name, "((PyObject *) %s)" % py_name)
def convert_python_to_c(self, wrapper):
"""See ReturnValue.convert_python_to_c"""
name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', "tmp_%s" % self.cpp_class.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+name])
value = self.transformation.transform(
self, wrapper.declarations, wrapper.after_call, "%s->obj" % name)
## now the hairy part :)
if self.caller_owns_return:
if not isinstance(self.cpp_class.memory_policy, cppclass.ReferenceCountingPolicy):
## the caller receives a copy
self.cpp_class.write_create_instance(wrapper.after_call,
"%s" % self.value,
'*'+value)
else:
## the caller gets a new reference to the same obj
self.cpp_class.memory_policy.write_incref(wrapper.after_call, value)
if self.type_traits.target_is_const:
wrapper.after_call.write_code(
"%s = const_cast< %s* >(%s);" %
(self.value, self.cpp_class.full_name, value))
else:
wrapper.after_call.write_code(
"%s = %s;" % (self.value, value))
else:
## caller gets a shared pointer
## but this is dangerous, avoid at all cost!!!
wrapper.after_call.write_code(
"// dangerous!\n%s = %s;" % (self.value, value))
warnings.warn("Returning shared pointers is dangerous!"
" The C++ API should be redesigned "
"to avoid this situation.")
def implement_parameter_custodians(wrapper):
"""
Generate the necessary code to implement the custodian=<N> option
of C++ class parameters. It accepts a Forward Wrapper as argument.
"""
assert isinstance(wrapper, ForwardWrapperBase)
for param in wrapper.parameters:
if not isinstance(param, CppClassPtrParameter):
continue
if param.custodian is None:
continue
if param.custodian == -1: # the custodian is the return value
assert wrapper.return_value.cpp_class.allow_subclassing
_add_ward(wrapper, "((PyObject *) %s)" % wrapper.return_value.py_name,
"((PyObject *) %s)" % param.py_name)
elif param.custodian == 0: # the custodian is the method self
assert wrapper.class_.allow_subclassing
_add_ward(wrapper, "((PyObject *) self)",
"((PyObject *) %s)" % param.py_name)
else: # the custodian is another parameter
assert param.custodian > 0
custodian_param = wrapper.parameters[param.custodian - 1]
assert custodian_param.cpp_class.allow_subclassing
_add_ward(wrapper, "((PyObject *) %s)" % custodian_param.py_name,
"((PyObject *) %s)" % param.py_name)