921 lines
37 KiB
Python
921 lines
37 KiB
Python
"""
|
|
Wrap C++ class methods and constructods.
|
|
"""
|
|
|
|
import warnings
|
|
import traceback
|
|
from copy import copy
|
|
|
|
from typehandlers.base import ForwardWrapperBase, ReverseWrapperBase, \
|
|
join_ctype_and_name, CodeGenerationError
|
|
from typehandlers.base import ReturnValue, Parameter
|
|
from typehandlers import codesink
|
|
import overloading
|
|
import settings
|
|
import utils
|
|
|
|
class CppMethod(ForwardWrapperBase):
|
|
"""
|
|
Class that generates a wrapper to a C++ class method
|
|
"""
|
|
|
|
def __init__(self, method_name, return_value, parameters, is_static=False,
|
|
template_parameters=(), is_virtual=False, is_const=False,
|
|
unblock_threads=None, is_pure_virtual=False,
|
|
custom_template_method_name=None, visibility='public',
|
|
custom_name=None, deprecated=False):
|
|
"""
|
|
Create an object the generates code to wrap a C++ class method.
|
|
|
|
@param return_value: the method return value
|
|
@type return_value: L{ReturnValue}
|
|
|
|
@param method_name: name of the method
|
|
|
|
@param parameters: the method parameters
|
|
@type parameters: list of L{Parameter}
|
|
|
|
@param is_static: whether it is a static method
|
|
|
|
@param template_parameters: optional list of template parameters needed to invoke the method
|
|
@type template_parameters: list of strings, each element a template parameter expression
|
|
|
|
@param is_virtual: whether the method is virtual (pure or not)
|
|
|
|
@param is_const: whether the method has a const modifier on it
|
|
|
|
@param unblock_threads: whether to release the Python GIL
|
|
around the method call or not. If None or omitted, use global
|
|
settings. Releasing the GIL has a small performance penalty,
|
|
but is recommended if the method is expected to take
|
|
considerable time to complete, because otherwise no other
|
|
Python thread is allowed to run until the method completes.
|
|
|
|
@param is_pure_virtual: whether the method is defined as "pure
|
|
virtual", i.e. virtual method with no default implementation
|
|
in the class being wrapped.
|
|
|
|
@param custom_name: alternate name to give to the method, in python side.
|
|
|
|
@param custom_template_method_name: (deprecated) same as parameter 'custom_name'.
|
|
|
|
@param visibility: visibility of the method within the C++ class
|
|
@type visibility: a string (allowed values are 'public', 'protected', 'private')
|
|
|
|
@param deprecated: deprecation state for this API:
|
|
- False: Not deprecated
|
|
- True: Deprecated
|
|
- "message": Deprecated, and deprecation warning contains the given message
|
|
"""
|
|
self.stack_where_defined = traceback.extract_stack()
|
|
|
|
## backward compatibility check
|
|
if isinstance(return_value, str) and isinstance(method_name, ReturnValue):
|
|
warnings.warn("CppMethod has changed API; see the API documentation (but trying to correct...)",
|
|
DeprecationWarning, stacklevel=2)
|
|
method_name, return_value = return_value, method_name
|
|
|
|
if return_value is None:
|
|
return_value = ReturnValue.new('void')
|
|
|
|
if unblock_threads is None:
|
|
unblock_threads = settings.unblock_threads
|
|
|
|
assert visibility in ['public', 'protected', 'private']
|
|
self.visibility = visibility
|
|
self.method_name = method_name
|
|
self.is_static = is_static
|
|
self.is_virtual = is_virtual
|
|
self.is_pure_virtual = is_pure_virtual
|
|
self.is_const = is_const
|
|
self.template_parameters = template_parameters
|
|
|
|
self.custom_name = (custom_name or custom_template_method_name)
|
|
|
|
#self.static_decl = True
|
|
self._class = None
|
|
self._helper_class = None
|
|
self.docstring = None
|
|
self.wrapper_base_name = None
|
|
self.wrapper_actual_name = None
|
|
self.return_value = None
|
|
self.parameters = None
|
|
|
|
return_value = utils.eval_retval(return_value, self)
|
|
parameters = [utils.eval_param(param, self) for param in parameters]
|
|
|
|
super(CppMethod, self).__init__(
|
|
return_value, parameters,
|
|
"return NULL;", "return NULL;",
|
|
unblock_threads=unblock_threads)
|
|
self.deprecated = deprecated
|
|
|
|
def set_helper_class(self, helper_class):
|
|
"Set the C++ helper class, which is used for overriding virtual methods"
|
|
self._helper_class = helper_class
|
|
self.wrapper_base_name = "%s::_wrap_%s" % (self._helper_class.name, self.method_name)
|
|
def get_helper_class(self):
|
|
"Get the C++ helper class, which is used for overriding virtual methods"
|
|
return self._helper_class
|
|
helper_class = property(get_helper_class, set_helper_class)
|
|
|
|
|
|
def matches_signature(self, other):
|
|
if self.mangled_name != other.mangled_name:
|
|
return False
|
|
if self.return_value.ctype != other.return_value.ctype:
|
|
return False
|
|
if len(self.parameters) != len(other.parameters):
|
|
return False
|
|
for param1, param2 in zip(self.parameters, other.parameters):
|
|
if param1.ctype != param2.ctype:
|
|
return False
|
|
if bool(self.is_const) != bool(other.is_const):
|
|
return False
|
|
return True
|
|
|
|
def set_custom_name(self, custom_name):
|
|
if custom_name is None:
|
|
self.mangled_name = utils.get_mangled_name(self.method_name, self.template_parameters)
|
|
else:
|
|
self.mangled_name = custom_name
|
|
|
|
custom_name = property(None, set_custom_name)
|
|
|
|
def clone(self):
|
|
"""Creates a semi-deep copy of this method wrapper. The returned
|
|
method wrapper clone contains copies of all parameters, so
|
|
they can be modified at will.
|
|
"""
|
|
meth = CppMethod(self.method_name,
|
|
self.return_value,
|
|
[copy(param) for param in self.parameters],
|
|
is_static=self.is_static,
|
|
template_parameters=self.template_parameters,
|
|
is_virtual=self.is_virtual,
|
|
is_pure_virtual=self.is_pure_virtual,
|
|
is_const=self.is_const,
|
|
visibility=self.visibility)
|
|
meth._class = self._class
|
|
meth.docstring = self.docstring
|
|
meth.wrapper_base_name = self.wrapper_base_name
|
|
meth.wrapper_actual_name = self.wrapper_actual_name
|
|
return meth
|
|
|
|
def set_class(self, class_):
|
|
"""set the class object this method belongs to"""
|
|
self._class = class_
|
|
self.wrapper_base_name = "_wrap_%s_%s" % (
|
|
class_.pystruct, self.mangled_name)
|
|
def get_class(self):
|
|
"""get the class object this method belongs to"""
|
|
return self._class
|
|
class_ = property(get_class, set_class)
|
|
|
|
def generate_call(self, class_=None):
|
|
"virtual method implementation; do not call"
|
|
#assert isinstance(class_, CppClass)
|
|
if class_ is None:
|
|
class_ = self._class
|
|
if self.template_parameters:
|
|
template_params = '< %s >' % ', '.join(self.template_parameters)
|
|
else:
|
|
template_params = ''
|
|
if self.is_static:
|
|
method = '%s::%s%s' % (class_.full_name, self.method_name, template_params)
|
|
else:
|
|
method = 'self->obj->%s%s' % (self.method_name, template_params)
|
|
if self.return_value.ctype == 'void':
|
|
self.before_call.write_code(
|
|
'%s(%s);' %
|
|
(method, ", ".join(self.call_params)))
|
|
else:
|
|
if self.return_value.REQUIRES_ASSIGNMENT_CONSTRUCTOR:
|
|
self.before_call.write_code(
|
|
'%s retval = %s(%s);' %
|
|
(self.return_value.ctype, method, ", ".join(self.call_params)))
|
|
else:
|
|
self.before_call.write_code(
|
|
'retval = %s(%s);' %
|
|
(method, ", ".join(self.call_params)))
|
|
|
|
def _before_return_hook(self):
|
|
"""hook that post-processes parameters and check for custodian=<n>
|
|
CppClass parameters"""
|
|
cppclass_typehandlers.implement_parameter_custodians(self)
|
|
|
|
def _get_pystruct(self):
|
|
# When a method is used in the context of a helper class, we
|
|
# should use the pystruct of the helper class' class.
|
|
#
|
|
# self.class_: original base class where the method is defined
|
|
# self._helper_class.class_: subclass that is inheriting the method
|
|
#
|
|
if self._helper_class is None:
|
|
return self.class_.pystruct
|
|
else:
|
|
return self._helper_class.class_.pystruct
|
|
|
|
def get_wrapper_signature(self, wrapper_name, extra_wrapper_params=()):
|
|
flags = self.get_py_method_def_flags()
|
|
|
|
self.wrapper_actual_name = wrapper_name
|
|
self.wrapper_return = "PyObject *"
|
|
if 'METH_STATIC' in flags:
|
|
_self_name = 'PYBINDGEN_UNUSED(dummy)'
|
|
else:
|
|
_self_name = 'self'
|
|
|
|
# if extra_wrapper_params:
|
|
# extra = ', '.join([''] + list(extra_wrapper_params))
|
|
# else:
|
|
# extra = ''
|
|
|
|
if 'METH_VARARGS' in flags:
|
|
if 'METH_KEYWORDS' in flags:
|
|
self.wrapper_args = ["%s *%s" % (self._get_pystruct(), _self_name),
|
|
"PyObject *args", "PyObject *kwargs"]
|
|
else:
|
|
assert not extra_wrapper_params, \
|
|
"extra_wrapper_params can only be used with"\
|
|
" full varargs/kwargs wrappers"
|
|
self.wrapper_args = ["%s *%s" % (self._get_pystruct(), _self_name),
|
|
"PyObject *args"]
|
|
else:
|
|
assert not extra_wrapper_params, \
|
|
"extra_wrapper_params can only be used with full varargs/kwargs wrappers"
|
|
if 'METH_STATIC' in flags:
|
|
self.wrapper_args = ['void']
|
|
else:
|
|
self.wrapper_args = ["%s *%s" % (self._get_pystruct(), _self_name)]
|
|
self.wrapper_args.extend(extra_wrapper_params)
|
|
|
|
return self.wrapper_return, "%s(%s)" % (self.wrapper_actual_name, ', '.join(self.wrapper_args))
|
|
|
|
def generate(self, code_sink, wrapper_name=None, extra_wrapper_params=()):
|
|
"""
|
|
Generates the wrapper code
|
|
code_sink -- a CodeSink instance that will receive the generated code
|
|
method_name -- actual name the method will get
|
|
extra_wrapper_params -- extra parameters the wrapper function should receive
|
|
|
|
Returns the corresponding PyMethodDef entry string.
|
|
"""
|
|
class_ = self.class_
|
|
#assert isinstance(class_, CppClass)
|
|
tmp_sink = codesink.MemoryCodeSink()
|
|
|
|
self.generate_body(tmp_sink, gen_call_params=[class_])
|
|
|
|
if wrapper_name is None:
|
|
self.wrapper_actual_name = self.wrapper_base_name
|
|
else:
|
|
self.wrapper_actual_name = wrapper_name
|
|
|
|
self.get_wrapper_signature(self.wrapper_actual_name, extra_wrapper_params)
|
|
self.write_open_wrapper(code_sink)#, add_static=self.static_decl)
|
|
tmp_sink.flush_to(code_sink)
|
|
self.write_close_wrapper(code_sink)
|
|
|
|
def get_py_method_def_flags(self):
|
|
"Get the PyMethodDef flags suitable for this method"
|
|
flags = super(CppMethod, self).get_py_method_def_flags()
|
|
if self.is_static:
|
|
flags.append('METH_STATIC')
|
|
return flags
|
|
|
|
def get_py_method_def(self, method_name):
|
|
"Get the PyMethodDef entry suitable for this method"
|
|
flags = self.get_py_method_def_flags()
|
|
return "{(char *) \"%s\", (PyCFunction) %s, %s, %s }," % \
|
|
(method_name, self.wrapper_actual_name, '|'.join(flags),
|
|
(self.docstring is None and "NULL" or ('"'+self.docstring+'"')))
|
|
|
|
def __str__(self):
|
|
|
|
if self.class_ is None:
|
|
cls_name = "???"
|
|
else:
|
|
cls_name = self.class_.full_name
|
|
if self.is_const:
|
|
const = ' const'
|
|
else:
|
|
const = ''
|
|
if self.is_virtual:
|
|
virtual = "virtual "
|
|
else:
|
|
virtual = ''
|
|
if self.is_pure_virtual:
|
|
pure_virtual = " = 0"
|
|
else:
|
|
pure_virtual = ''
|
|
|
|
if self.return_value is None:
|
|
retval = "retval?"
|
|
else:
|
|
retval = self.return_value.ctype
|
|
|
|
if self.parameters is None:
|
|
params = 'params?'
|
|
else:
|
|
params = ', '.join(["%s %s" % (param.ctype, param.name) for param in self.parameters])
|
|
|
|
return ("%s: %s%s %s::%s (%s)%s%s;" %
|
|
(self.visibility, virtual, retval, cls_name, self.method_name,
|
|
params, const, pure_virtual))
|
|
|
|
|
|
class CppOverloadedMethod(overloading.OverloadedWrapper):
|
|
"Support class for overloaded methods"
|
|
RETURN_TYPE = 'PyObject *'
|
|
ERROR_RETURN = 'return NULL;'
|
|
|
|
|
|
|
|
class DummyReturnValue(ReturnValue):
|
|
CTYPES = []
|
|
"""
|
|
A 'dummy' return value object used for modelling methods that have
|
|
incomplete or incorrect parameters or return values.
|
|
"""
|
|
def __init__(self, arg):
|
|
"""
|
|
Accepts either a ReturnValue object or a tuple as sole
|
|
parameter. In case it's a tuple, it is assumed to be a retval
|
|
spec (*args, **kwargs).
|
|
"""
|
|
if isinstance(arg, ReturnValue):
|
|
super(DummyReturnValue, self).__init__(arg.ctype)
|
|
else:
|
|
args, kwargs = utils.parse_retval_spec(arg)
|
|
super(DummyReturnValue, self).__init__(args[0])
|
|
def convert_c_to_python(self, wrapper):
|
|
raise TypeError("this is a DummyReturnValue")
|
|
def convert_python_to_c(self, wrapper):
|
|
raise TypeError("this is a DummyReturnValue")
|
|
def get_c_error_return(self):
|
|
return ''
|
|
|
|
|
|
class DummyParameter(Parameter):
|
|
CTYPES = []
|
|
DIRECTIONS = [Parameter.DIRECTION_IN, Parameter.DIRECTION_OUT, Parameter.DIRECTION_INOUT]
|
|
"""
|
|
A 'dummy' parameter object used for modelling methods that have
|
|
incomplete or incorrect parameters or return values.
|
|
"""
|
|
def __init__(self, arg):
|
|
"""
|
|
Accepts either a Parameter object or a tuple as sole
|
|
parameter. In case it's a tuple, it is assumed to be a retval
|
|
spec (*args, **kwargs).
|
|
"""
|
|
if isinstance(arg, ReturnValue):
|
|
super(DummyParameter, self).__init__(arg.ctype)
|
|
else:
|
|
args, kwargs = utils.parse_param_spec(arg)
|
|
super(DummyParameter, self).__init__(args[0], args[1])
|
|
|
|
def convert_c_to_python(self, wrapper):
|
|
raise TypeError("this is a DummyParameter")
|
|
def convert_python_to_c(self, wrapper):
|
|
raise TypeError("this is a DummyParameter")
|
|
|
|
|
|
class CppDummyMethod(CppMethod):
|
|
"""
|
|
A 'dummy' method; cannot be generated due to incomple or incorrect
|
|
parameters, but is added to the class to model the missing method.
|
|
"""
|
|
|
|
def __init__(self, method_name, return_value, parameters, *args, **kwargs):
|
|
return_value = DummyReturnValue(return_value)
|
|
parameters = [DummyParameter(p) for p in parameters]
|
|
super(CppDummyMethod, self).__init__(method_name, return_value, parameters, *args, **kwargs)
|
|
|
|
|
|
class CppConstructor(ForwardWrapperBase):
|
|
"""
|
|
Class that generates a wrapper to a C++ class constructor. Such
|
|
wrapper is used as the python class __init__ method.
|
|
"""
|
|
|
|
def __init__(self, parameters, unblock_threads=None, visibility='public', deprecated=False):
|
|
"""
|
|
@param parameters: the constructor parameters
|
|
|
|
@param deprecated: deprecation state for this API:
|
|
- False: Not deprecated
|
|
- True: Deprecated
|
|
- "message": Deprecated, and deprecation warning contains the given message
|
|
"""
|
|
self.stack_where_defined = traceback.extract_stack()
|
|
if unblock_threads is None:
|
|
unblock_threads = settings.unblock_threads
|
|
|
|
parameters = [utils.eval_param(param, self) for param in parameters]
|
|
|
|
super(CppConstructor, self).__init__(
|
|
None, parameters,
|
|
"return -1;", "return -1;",
|
|
force_parse=ForwardWrapperBase.PARSE_TUPLE_AND_KEYWORDS,
|
|
unblock_threads=unblock_threads)
|
|
self.deprecated = deprecated
|
|
assert visibility in ['public', 'protected', 'private']
|
|
self.visibility = visibility
|
|
self.wrapper_base_name = None
|
|
self.wrapper_actual_name = None
|
|
self._class = None
|
|
|
|
def clone(self):
|
|
"""Creates a semi-deep copy of this constructor wrapper. The returned
|
|
constructor wrapper clone contains copies of all parameters, so
|
|
they can be modified at will.
|
|
"""
|
|
meth = type(self)([copy(param) for param in self.parameters])
|
|
meth._class = self._class
|
|
meth.wrapper_base_name = self.wrapper_base_name
|
|
meth.wrapper_actual_name = self.wrapper_actual_name
|
|
return meth
|
|
|
|
def set_class(self, class_):
|
|
"Set the class wrapper object (CppClass)"
|
|
self._class = class_
|
|
self.wrapper_base_name = "_wrap_%s__tp_init" % (
|
|
class_.pystruct,)
|
|
def get_class(self):
|
|
"Get the class wrapper object (CppClass)"
|
|
return self._class
|
|
class_ = property(get_class, set_class)
|
|
|
|
def generate_call(self, class_=None):
|
|
"virtual method implementation; do not call"
|
|
if class_ is None:
|
|
class_ = self._class
|
|
#assert isinstance(class_, CppClass)
|
|
if class_.helper_class is None:
|
|
class_.write_create_instance(self.before_call, "self->obj", ", ".join(self.call_params))
|
|
else:
|
|
## We should only create a helper class instance when
|
|
## being called from a user python subclass.
|
|
self.before_call.write_code("if (self->ob_type != &%s)" % class_.pytypestruct)
|
|
self.before_call.write_code("{")
|
|
self.before_call.indent()
|
|
|
|
class_.write_create_instance(self.before_call, "self->obj", ", ".join(self.call_params),
|
|
class_.helper_class.name)
|
|
self.before_call.write_code('((%s*) self->obj)->set_pyobj((PyObject *)self);'
|
|
% class_.helper_class.name)
|
|
|
|
self.before_call.unindent()
|
|
self.before_call.write_code("} else {")
|
|
self.before_call.indent()
|
|
|
|
self.before_call.write_code("// visibility: %r" % self.visibility)
|
|
try:
|
|
if self.visibility not in ['public']:
|
|
raise CodeGenerationError("private/protected constructor")
|
|
class_.get_construct_name()
|
|
except CodeGenerationError:
|
|
self.before_call.write_code('PyErr_SetString(PyExc_TypeError, "class \'%s\' '
|
|
'cannot be constructed");' % class_.name)
|
|
self.before_call.write_code('return -1;')
|
|
else:
|
|
class_.write_create_instance(self.before_call, "self->obj", ", ".join(self.call_params))
|
|
|
|
self.before_call.unindent()
|
|
self.before_call.write_code("}")
|
|
|
|
def _before_return_hook(self):
|
|
"hook that post-processes parameters and check for custodian=<n> CppClass parameters"
|
|
cppclass_typehandlers.implement_parameter_custodians(self)
|
|
|
|
def generate(self, code_sink, wrapper_name=None, extra_wrapper_params=()):
|
|
"""
|
|
Generates the wrapper code
|
|
code_sink -- a CodeSink instance that will receive the generated code
|
|
class_ -- the c++ class wrapper the method belongs to
|
|
|
|
Returns the wrapper function name.
|
|
"""
|
|
if self.visibility == 'private':
|
|
raise CodeGenerationError("private constructor")
|
|
elif self.visibility == 'protected':
|
|
if self._class.helper_class is None:
|
|
raise CodeGenerationError("protected constructor and no helper class")
|
|
|
|
#assert isinstance(class_, CppClass)
|
|
tmp_sink = codesink.MemoryCodeSink()
|
|
|
|
assert self._class is not None
|
|
self.generate_body(tmp_sink, gen_call_params=[self._class])
|
|
|
|
assert ((self.parse_params.get_parameters() == ['""'])
|
|
or self.parse_params.get_keywords() is not None), \
|
|
("something went wrong with the type handlers;"
|
|
" constructors need parameter names, "
|
|
"yet no names were given for the class %s constructor"
|
|
% self._class.name)
|
|
|
|
if wrapper_name is None:
|
|
self.wrapper_actual_name = self.wrapper_base_name
|
|
else:
|
|
self.wrapper_actual_name = wrapper_name
|
|
|
|
self.wrapper_return = 'static int'
|
|
self.wrapper_args = ["%s *self" % self._class.pystruct,
|
|
"PyObject *args", "PyObject *kwargs"]
|
|
self.wrapper_args.extend(extra_wrapper_params)
|
|
|
|
self.write_open_wrapper(code_sink)
|
|
tmp_sink.flush_to(code_sink)
|
|
code_sink.writeln('return 0;')
|
|
self.write_close_wrapper(code_sink)
|
|
|
|
|
|
class CppFunctionAsConstructor(CppConstructor):
|
|
"""
|
|
Class that generates a wrapper to a C/C++ function that appears as a contructor.
|
|
"""
|
|
def __init__(self, c_function_name, return_value, parameters, unblock_threads=None):
|
|
"""
|
|
@param c_function_name: name of the C/C++ function; FIXME: for now it is
|
|
implied that this function returns a pointer to the a class
|
|
instance with caller_owns_return=True semantics.
|
|
|
|
@param return_value: function return value type
|
|
@type return_value: L{ReturnValue}
|
|
|
|
@param parameters: the function/constructor parameters
|
|
@type parameters: list of L{Parameter}
|
|
|
|
"""
|
|
if unblock_threads is None:
|
|
unblock_threads = settings.unblock_threads
|
|
|
|
parameters = [utils.eval_param(param, self) for param in parameters]
|
|
|
|
super(CppFunctionAsConstructor, self).__init__(parameters)
|
|
self.c_function_name = c_function_name
|
|
self.function_return_value = return_value
|
|
|
|
def generate_call(self, class_=None):
|
|
"virtual method implementation; do not call"
|
|
if class_ is None:
|
|
class_ = self._class
|
|
#assert isinstance(class_, CppClass)
|
|
assert class_.helper_class is None
|
|
## FIXME: check caller_owns_return in self.function_return_value
|
|
self.before_call.write_code("self->obj = %s(%s);" %
|
|
(self.c_function_name, ", ".join(self.call_params)))
|
|
|
|
|
|
class CppOverloadedConstructor(overloading.OverloadedWrapper):
|
|
"Support class for overloaded constructors"
|
|
RETURN_TYPE = 'int'
|
|
ERROR_RETURN = 'return -1;'
|
|
|
|
|
|
class CppNoConstructor(ForwardWrapperBase):
|
|
"""
|
|
|
|
Class that generates a constructor that raises an exception saying
|
|
that the class has no constructor.
|
|
|
|
"""
|
|
|
|
def __init__(self, reason):
|
|
"""
|
|
reason -- string indicating reason why the class cannot be constructed.
|
|
"""
|
|
self.stack_where_defined = traceback.extract_stack()
|
|
super(CppNoConstructor, self).__init__(
|
|
None, [],
|
|
"return -1;", "return -1;")
|
|
self.reason = reason
|
|
|
|
def generate_call(self):
|
|
"dummy method, not really called"
|
|
pass
|
|
|
|
def generate(self, code_sink, class_):
|
|
"""
|
|
Generates the wrapper code
|
|
code_sink -- a CodeSink instance that will receive the generated code
|
|
class_ -- the c++ class wrapper the method belongs to
|
|
|
|
Returns the wrapper function name.
|
|
"""
|
|
#assert isinstance(class_, CppClass)
|
|
|
|
self.wrapper_actual_name = "_wrap_%s__tp_init" % (
|
|
class_.pystruct,)
|
|
self.wrapper_return = 'static int'
|
|
self.wrapper_args = ["void"]
|
|
|
|
self.write_open_wrapper(code_sink)
|
|
|
|
code_sink.writeln('PyErr_SetString(PyExc_TypeError, "class \'%s\' '
|
|
'cannot be constructed (%s)");' % (class_.name, self.reason))
|
|
code_sink.writeln('return -1;')
|
|
|
|
self.write_close_wrapper(code_sink)
|
|
|
|
|
|
class CppVirtualMethodParentCaller(CppMethod):
|
|
"""
|
|
Class that generates a wrapper that calls a virtual method default
|
|
implementation in a parent base class.
|
|
"""
|
|
|
|
def __init__(self, method, unblock_threads=None):
|
|
"""
|
|
"""
|
|
super(CppVirtualMethodParentCaller, self).__init__(
|
|
method.method_name, method.return_value, method.parameters, unblock_threads=unblock_threads)
|
|
#self.static_decl = False
|
|
self.method = method
|
|
|
|
def get_class(self):
|
|
return self.method.class_
|
|
class_ = property(get_class)
|
|
|
|
#def set_class(self, class_):
|
|
# "Set the class wrapper object (CppClass)"
|
|
# self._class = class_
|
|
|
|
def generate_declaration(self, code_sink, extra_wrapper_parameters=()):
|
|
## We need to fake generate the code (and throw away the
|
|
## result) only in order to obtain correct method signature.
|
|
self.reset_code_generation_state()
|
|
self.generate(codesink.NullCodeSink(), extra_wrapper_params=extra_wrapper_parameters)
|
|
assert isinstance(self.wrapper_return, str)
|
|
assert isinstance(self.wrapper_actual_name, str)
|
|
assert isinstance(self.wrapper_args, list)
|
|
code_sink.writeln('%s %s(%s);' % (self.wrapper_return, self.wrapper_actual_name, ', '.join(self.wrapper_args)))
|
|
self.reset_code_generation_state()
|
|
|
|
def generate_class_declaration(self, code_sink, extra_wrapper_parameters=()):
|
|
## We need to fake generate the code (and throw away the
|
|
## result) only in order to obtain correct method signature.
|
|
self.reset_code_generation_state()
|
|
self.generate(codesink.NullCodeSink(), extra_wrapper_params=extra_wrapper_parameters)
|
|
assert isinstance(self.wrapper_return, str)
|
|
assert isinstance(self.wrapper_actual_name, str)
|
|
assert isinstance(self.wrapper_args, list)
|
|
dummy_cls, name = self.wrapper_actual_name.split('::')
|
|
code_sink.writeln('static %s %s(%s);' % (self.wrapper_return, name, ', '.join(self.wrapper_args)))
|
|
self.reset_code_generation_state()
|
|
|
|
def generate_parent_caller_method(self, code_sink):
|
|
## generate a '%s__parent_caller' method (static methods
|
|
## cannot "conquer" 'protected' access type, only regular
|
|
## instance methods).
|
|
code_sink.writeln('inline %s %s__parent_caller(%s)' % (
|
|
self.return_value.ctype,
|
|
self.method_name,
|
|
', '.join([join_ctype_and_name(param.ctype, param.name) for param in self.parameters])
|
|
))
|
|
if self.return_value.ctype == 'void':
|
|
code_sink.writeln('{ %s::%s(%s); }' % (self.method.class_.full_name, self.method_name,
|
|
', '.join([param.name for param in self.parameters])))
|
|
else:
|
|
code_sink.writeln('{ return %s::%s(%s); }' % (self.method.class_.full_name, self.method_name,
|
|
', '.join([param.name for param in self.parameters])))
|
|
|
|
|
|
def generate_call(self, class_=None):
|
|
"virtual method implementation; do not call"
|
|
class_ = self.class_
|
|
method = 'reinterpret_cast< %s* >(self->obj)->%s__parent_caller' % (self._helper_class.name, self.method_name)
|
|
if self.return_value.ctype == 'void':
|
|
self.before_call.write_code(
|
|
'%s(%s);' %
|
|
(method, ", ".join(self.call_params)))
|
|
else:
|
|
if self.return_value.REQUIRES_ASSIGNMENT_CONSTRUCTOR:
|
|
self.before_call.write_code(
|
|
'%s retval = %s(%s);' %
|
|
(self.return_value.ctype, method, ", ".join(self.call_params)))
|
|
else:
|
|
self.before_call.write_code(
|
|
'retval = %s(%s);' %
|
|
(method, ", ".join(self.call_params)))
|
|
|
|
def get_py_method_def(self, method_name=None):
|
|
"Get the PyMethodDef entry suitable for this method"
|
|
|
|
assert self.wrapper_actual_name == self.wrapper_base_name, \
|
|
"wrapper_actual_name=%r but wrapper_base_name=%r" % \
|
|
(self.wrapper_actual_name, self.wrapper_base_name)
|
|
assert self._helper_class is not None
|
|
if method_name is None:
|
|
method_name = self.method_name
|
|
flags = self.get_py_method_def_flags()
|
|
return "{(char *) \"%s\", (PyCFunction) %s, %s, %s }," % \
|
|
(method_name,
|
|
self.wrapper_actual_name,#'::'.join((self._helper_class.name, self.wrapper_actual_name)),
|
|
'|'.join(flags),
|
|
(self.docstring is None and "NULL" or ('"'+self.docstring+'"')))
|
|
|
|
def clone(self):
|
|
"""
|
|
Creates a semi-deep copy of this method wrapper. The returned
|
|
method wrapper clone contains copies of all parameters, so
|
|
they can be modified at will.
|
|
"""
|
|
meth = CppVirtualMethodParentCaller(
|
|
self.return_value,
|
|
self.method_name,
|
|
[copy(param) for param in self.parameters])
|
|
#meth._class = self._class
|
|
meth.method = self.method
|
|
meth._helper_class = self._helper_class
|
|
meth.docstring = self.docstring
|
|
meth.wrapper_base_name = self.wrapper_base_name
|
|
meth.wrapper_actual_name = self.wrapper_actual_name
|
|
return meth
|
|
|
|
|
|
class CppVirtualMethodProxy(ReverseWrapperBase):
|
|
"""
|
|
Class that generates a proxy virtual method that calls a similarly named python method.
|
|
"""
|
|
|
|
def __init__(self, method):
|
|
"""
|
|
xxx
|
|
"""
|
|
self.stack_where_defined = traceback.extract_stack()
|
|
super(CppVirtualMethodProxy, self).__init__(method.return_value, method.parameters)
|
|
self.method_name = method.method_name
|
|
self.method = method
|
|
self._helper_class = None
|
|
|
|
def get_class(self):
|
|
"Get the class wrapper object (CppClass)"
|
|
return self.method.class_
|
|
class_ = property(get_class)
|
|
|
|
def set_helper_class(self, helper_class):
|
|
"Set the C++ helper class, which is used for overriding virtual methods"
|
|
self._helper_class = helper_class
|
|
self.wrapper_base_name = "_wrap_%s" % self.method_name
|
|
def get_helper_class(self):
|
|
"Get the C++ helper class, which is used for overriding virtual methods"
|
|
return self._helper_class
|
|
helper_class = property(get_helper_class, set_helper_class)
|
|
|
|
|
|
def generate_python_call(self):
|
|
"""code to call the python method"""
|
|
params = ['m_pyself', '(char *) "_%s"' % self.method_name]
|
|
build_params = self.build_params.get_parameters()
|
|
if build_params[0][0] == '"':
|
|
build_params[0] = '(char *) ' + build_params[0]
|
|
params.extend(build_params)
|
|
self.before_call.write_code('py_retval = PyObject_CallMethod(%s);'
|
|
% (', '.join(params),))
|
|
self.before_call.write_error_check('py_retval == NULL', failure_cleanup='PyErr_Print();')
|
|
self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
|
|
|
|
def generate_declaration(self, code_sink):
|
|
if self.method.is_const:
|
|
decl_post_modifiers = ' const'
|
|
else:
|
|
decl_post_modifiers = ''
|
|
|
|
params_list = ', '.join([join_ctype_and_name(param.ctype, param.name)
|
|
for param in self.parameters])
|
|
code_sink.writeln("virtual %s %s(%s)%s;" %
|
|
(self.return_value.ctype, self.method_name, params_list,
|
|
decl_post_modifiers))
|
|
|
|
|
|
def generate(self, code_sink):
|
|
"""generates the proxy virtual method"""
|
|
if self.method.is_const:
|
|
decl_post_modifiers = ['const']
|
|
else:
|
|
decl_post_modifiers = []
|
|
|
|
## if the python subclass doesn't define a virtual method,
|
|
## just chain to parent class and don't do anything else
|
|
call_params = ', '.join([param.name for param in self.parameters])
|
|
self.before_call.write_code(
|
|
r'if (!PyObject_HasAttrString(m_pyself, (char *) "_%s")) {' % self.method_name)
|
|
if self.return_value.ctype == 'void':
|
|
if not (self.method.is_pure_virtual or self.method.visibility == 'private'):
|
|
self.before_call.write_code(r' %s::%s(%s);'
|
|
% (self.class_.full_name, self.method_name, call_params))
|
|
self.before_call.write_cleanup()
|
|
self.before_call.write_code(r' return;')
|
|
else:
|
|
if self.method.is_pure_virtual or self.method.visibility == 'private':
|
|
if isinstance(self.return_value, cppclass.CppClassReturnValue) \
|
|
and self.return_value.cpp_class.has_trivial_constructor:
|
|
pass
|
|
else:
|
|
self.set_error_return('''
|
|
PyErr_Print();
|
|
Py_FatalError("Error detected, but parent virtual is pure virtual or private virtual, "
|
|
"and return is a class without trival constructor");''')
|
|
else:
|
|
self.set_error_return("return %s::%s(%s);"
|
|
% (self.class_.full_name, self.method_name, call_params))
|
|
self.before_call.indent()
|
|
self.before_call.write_cleanup()
|
|
self.before_call.write_code(self.error_return)
|
|
self.before_call.unindent()
|
|
self.before_call.write_code('}')
|
|
|
|
## Set "m_pyself->obj = this" around virtual method call invocation
|
|
self_obj_before = self.declarations.declare_variable(
|
|
'%s*' % self.class_.full_name, 'self_obj_before')
|
|
self.before_call.write_code("%s = reinterpret_cast< %s* >(m_pyself)->obj;" %
|
|
(self_obj_before, self.class_.pystruct))
|
|
if self.method.is_const:
|
|
this_expression = ("const_cast< %s* >((const %s*) this)" %
|
|
(self.class_.full_name, self.class_.full_name))
|
|
else:
|
|
this_expression = "(%s*) this" % (self.class_.full_name)
|
|
self.before_call.write_code("reinterpret_cast< %s* >(m_pyself)->obj = %s;" %
|
|
(self.class_.pystruct, this_expression))
|
|
self.before_call.add_cleanup_code("reinterpret_cast< %s* >(m_pyself)->obj = %s;" %
|
|
(self.class_.pystruct, self_obj_before))
|
|
|
|
super(CppVirtualMethodProxy, self).generate(
|
|
code_sink, '::'.join((self._helper_class.name, self.method_name)),
|
|
decl_modifiers=[],
|
|
decl_post_modifiers=decl_post_modifiers)
|
|
|
|
|
|
|
|
class CustomCppMethodWrapper(CppMethod):
|
|
"""
|
|
Adds a custom method wrapper. The custom wrapper must be
|
|
prepared to support overloading, i.e. it must have an additional
|
|
"PyObject **return_exception" parameter, and raised exceptions
|
|
must be returned by this parameter.
|
|
"""
|
|
|
|
NEEDS_OVERLOADING_INTERFACE = True
|
|
|
|
def __init__(self, method_name, wrapper_name, wrapper_body=None,
|
|
flags=('METH_VARARGS', 'METH_KEYWORDS')):
|
|
super(CustomCppMethodWrapper, self).__init__(method_name, ReturnValue.new('void'), [])
|
|
self.wrapper_base_name = wrapper_name
|
|
self.wrapper_actual_name = wrapper_name
|
|
self.meth_flags = list(flags)
|
|
self.wrapper_body = wrapper_body
|
|
|
|
|
|
def generate(self, code_sink, dummy_wrapper_name=None, extra_wrapper_params=()):
|
|
assert extra_wrapper_params == ["PyObject **return_exception"]
|
|
|
|
self.wrapper_args = ["%s *self" % self.class_.pystruct, "PyObject *args", "PyObject *kwargs", "PyObject **return_exception"]
|
|
self.wrapper_return = "PyObject *"
|
|
if self.wrapper_body is not None:
|
|
code_sink.writeln(self.wrapper_body)
|
|
else:
|
|
self.generate_declaration(code_sink, extra_wrapper_parameters=extra_wrapper_params)
|
|
|
|
def generate_declaration(self, code_sink, extra_wrapper_parameters=()):
|
|
assert isinstance(self.wrapper_return, str)
|
|
assert isinstance(self.wrapper_actual_name, str)
|
|
assert isinstance(self.wrapper_args, list)
|
|
code_sink.writeln('%s %s(%s);' % (self.wrapper_return, self.wrapper_actual_name, ', '.join(self.wrapper_args)))
|
|
|
|
def generate_call(self, *args, **kwargs):
|
|
pass
|
|
|
|
|
|
|
|
class CustomCppConstructorWrapper(CppConstructor):
|
|
"""
|
|
Adds a custom constructor wrapper. The custom wrapper must be
|
|
prepared to support overloading, i.e. it must have an additional
|
|
"PyObject **return_exception" parameter, and raised exceptions
|
|
must be returned by this parameter.
|
|
"""
|
|
|
|
NEEDS_OVERLOADING_INTERFACE = True
|
|
|
|
def __init__(self, wrapper_name, wrapper_body):
|
|
super(CustomCppConstructorWrapper, self).__init__([])
|
|
self.wrapper_base_name = wrapper_name
|
|
self.wrapper_actual_name = wrapper_name
|
|
self.wrapper_body = wrapper_body
|
|
|
|
def generate(self, code_sink, dummy_wrapper_name=None, extra_wrapper_params=()):
|
|
assert extra_wrapper_params == ["PyObject **return_exception"]
|
|
code_sink.writeln(self.wrapper_body)
|
|
return ("int %s (%s *self, PyObject *args, PyObject *kwargs, PyObject **return_exception)"
|
|
% (self.wrapper_actual_name, self.class_.pystruct))
|
|
|
|
def generate_call(self, *args, **kwargs):
|
|
pass
|
|
|
|
import cppclass
|
|
import cppclass_typehandlers
|