849 lines
33 KiB
Python
849 lines
33 KiB
Python
"""
|
|
Wrap C++ STL containers
|
|
"""
|
|
|
|
#import warnings
|
|
|
|
from typehandlers.base import ForwardWrapperBase, ReverseWrapperBase, \
|
|
Parameter, ReturnValue, param_type_matcher, return_type_matcher, \
|
|
TypeConfigurationError, NotSupportedError
|
|
|
|
from typehandlers import codesink
|
|
from pytypeobject import PyTypeObject
|
|
import settings
|
|
import utils
|
|
|
|
|
|
class IterNextWrapper(ForwardWrapperBase):
|
|
'''
|
|
tp_iternext wrapper
|
|
'''
|
|
|
|
HAVE_RETURN_VALUE = True
|
|
|
|
def __init__(self, container):
|
|
"""
|
|
value_type -- a ReturnValue object handling the value type;
|
|
container -- the L{Container}
|
|
"""
|
|
super(IterNextWrapper, self).__init__(
|
|
None, [], "return NULL;", "return NULL;", no_c_retval=True)
|
|
assert isinstance(container, Container)
|
|
self.container = container
|
|
self.c_function_name = "_wrap_%s__tp_iternext" % (self.container.iter_pystruct)
|
|
self.iter_variable_name = None
|
|
self.reset_code_generation_state()
|
|
#self.iter_variable_name = self.declarations.declare_variable(
|
|
# "%s::iterator" % container.full_name , 'iter')
|
|
#self.container.value_type.value = "(*%s)" % self.iter_variable_name
|
|
|
|
def reset_code_generation_state(self):
|
|
super(IterNextWrapper, self).reset_code_generation_state()
|
|
self.iter_variable_name = self.declarations.declare_variable(
|
|
"%s::iterator" % self.container.full_name , 'iter')
|
|
|
|
def generate_call(self):
|
|
self.before_call.write_code("%s = *self->iterator;" % (self.iter_variable_name,))
|
|
self.before_call.write_error_check(
|
|
"%s == self->container->obj->end()" % (self.iter_variable_name,),
|
|
"PyErr_SetNone(PyExc_StopIteration);")
|
|
self.before_call.write_code("++(*self->iterator);")
|
|
if self.container.key_type is None:
|
|
self.container.value_type.value = "(*%s)" % self.iter_variable_name
|
|
self.container.value_type.convert_c_to_python(self)
|
|
else:
|
|
self.container.value_type.value = "%s->second" % self.iter_variable_name
|
|
self.container.value_type.convert_c_to_python(self)
|
|
self.container.key_type.value = "%s->first" % self.iter_variable_name
|
|
self.container.key_type.convert_c_to_python(self)
|
|
|
|
def generate(self, code_sink):
|
|
"""
|
|
code_sink -- a CodeSink instance that will receive the generated code
|
|
"""
|
|
|
|
tmp_sink = codesink.MemoryCodeSink()
|
|
self.generate_body(tmp_sink)
|
|
code_sink.writeln("static PyObject* %s(%s *self)" % (self.c_function_name,
|
|
self.container.iter_pystruct))
|
|
code_sink.writeln('{')
|
|
code_sink.indent()
|
|
tmp_sink.flush_to(code_sink)
|
|
code_sink.unindent()
|
|
code_sink.writeln('}')
|
|
|
|
|
|
class ContainerTraits(object):
|
|
def __init__(self, add_value_method, is_mapping=False):
|
|
self.add_value_method = add_value_method
|
|
self.is_mapping = is_mapping
|
|
|
|
container_traits_list = {
|
|
'list': ContainerTraits(add_value_method='push_back'),
|
|
'deque': ContainerTraits(add_value_method='push_back'),
|
|
'queue': ContainerTraits(add_value_method='push'),
|
|
'priority_queue': ContainerTraits(add_value_method='push'),
|
|
'vector': ContainerTraits(add_value_method='push_back'),
|
|
'stack': ContainerTraits(add_value_method='push'),
|
|
'set': ContainerTraits(add_value_method='insert'),
|
|
'multiset': ContainerTraits(add_value_method='insert'),
|
|
'hash_set': ContainerTraits(add_value_method='insert'),
|
|
'hash_multiset': ContainerTraits(add_value_method='insert'),
|
|
'map': ContainerTraits(add_value_method='insert', is_mapping=True),
|
|
}
|
|
|
|
# from wikipedia: """Deque is sometimes written dequeue, but this use
|
|
# is generally deprecated in technical literature or technical writing
|
|
# because dequeue is also a verb meaning "to remove from a queue" """.
|
|
container_traits_list['dequeue'] = container_traits_list['deque']
|
|
|
|
class Container(object):
|
|
def __init__(self, name, value_type, container_type, outer_class=None, custom_name=None):
|
|
"""
|
|
:param name: C++ type name of the container, e.g. std::vector<int> or MyIntList
|
|
|
|
:param value_type: a ReturnValue of the element type: note,
|
|
for mapping containers, value_type is a tuple with two
|
|
ReturnValue's: (key, element).
|
|
|
|
:param container_type: a string with the type of container,
|
|
one of 'list', 'deque', 'queue', 'priority_queue',
|
|
'vector', 'stack', 'set', 'multiset', 'hash_set',
|
|
'hash_multiset', 'map'
|
|
|
|
:param outer_class: if the type is defined inside a class, must be a reference to the outer class
|
|
:type outer_class: None or L{CppClass}
|
|
|
|
:param custom_name: alternative name to register with in the Python module
|
|
|
|
"""
|
|
if '<' in name or '::' in name:
|
|
self.name = utils.mangle_name(name)
|
|
self.full_name = name
|
|
self._full_name_is_definitive = True
|
|
else:
|
|
self._full_name_is_definitive = False
|
|
self.full_name = None
|
|
self.name = name
|
|
self._module = None
|
|
self.outer_class = outer_class
|
|
self.mangled_name = None
|
|
self.mangled_full_name = None
|
|
self.container_traits = container_traits_list[container_type]
|
|
self.custom_name = custom_name
|
|
self._pystruct = None
|
|
self.pytypestruct = "***GIVE ME A NAME***"
|
|
self.pytype = PyTypeObject()
|
|
|
|
self.iter_pytypestruct = "***GIVE ME A NAME***"
|
|
self.iter_pytype = PyTypeObject()
|
|
self._iter_pystruct = None
|
|
|
|
if self.container_traits.is_mapping:
|
|
(key_type, value_type) = value_type
|
|
self.key_type = utils.eval_retval(key_type, self)
|
|
self.value_type = utils.eval_retval(value_type, self)
|
|
else:
|
|
self.key_type = None
|
|
self.value_type = utils.eval_retval(value_type, self)
|
|
self.python_to_c_converter = None
|
|
|
|
if name != 'dummy':
|
|
## register type handlers
|
|
|
|
class ThisContainerParameter(ContainerParameter):
|
|
"""Register this C++ container as pass-by-value parameter"""
|
|
CTYPES = []
|
|
container_type = self
|
|
self.ThisContainerParameter = ThisContainerParameter
|
|
try:
|
|
param_type_matcher.register(name, self.ThisContainerParameter)
|
|
except ValueError:
|
|
pass
|
|
|
|
class ThisContainerRefParameter(ContainerRefParameter):
|
|
"""Register this C++ container as pass-by-value parameter"""
|
|
CTYPES = []
|
|
container_type = self
|
|
self.ThisContainerRefParameter = ThisContainerRefParameter
|
|
try:
|
|
param_type_matcher.register(name+'&', self.ThisContainerRefParameter)
|
|
except ValueError:
|
|
pass
|
|
|
|
class ThisContainerPtrParameter(ContainerPtrParameter):
|
|
"""Register this C++ container as pass-by-ptr parameter"""
|
|
CTYPES = []
|
|
container_type = self
|
|
self.ThisContainerPtrParameter = ThisContainerPtrParameter
|
|
try:
|
|
param_type_matcher.register(name+'*', self.ThisContainerPtrParameter)
|
|
except ValueError:
|
|
pass
|
|
|
|
class ThisContainerReturn(ContainerReturnValue):
|
|
"""Register this C++ container as value return"""
|
|
CTYPES = []
|
|
container_type = self
|
|
self.ThisContainerReturn = ThisContainerReturn
|
|
self.ThisContainerRefReturn = ThisContainerReturn
|
|
try:
|
|
return_type_matcher.register(name, self.ThisContainerReturn)
|
|
return_type_matcher.register(name, self.ThisContainerRefReturn)
|
|
except ValueError:
|
|
pass
|
|
|
|
def __repr__(self):
|
|
return "<pybindgen.Container %r>" % self.full_name
|
|
|
|
def get_module(self):
|
|
"""Get the Module object this type belongs to"""
|
|
return self._module
|
|
|
|
def set_module(self, module):
|
|
"""Set the Module object this type belongs to"""
|
|
self._module = module
|
|
self._update_names()
|
|
|
|
module = property(get_module, set_module)
|
|
|
|
def get_pystruct(self):
|
|
if self._pystruct is None:
|
|
raise ValueError
|
|
return self._pystruct
|
|
pystruct = property(get_pystruct)
|
|
|
|
def get_iter_pystruct(self):
|
|
if self._iter_pystruct is None:
|
|
raise ValueError
|
|
return self._iter_pystruct
|
|
iter_pystruct = property(get_iter_pystruct)
|
|
|
|
def _update_names(self):
|
|
|
|
prefix = settings.name_prefix.capitalize()
|
|
|
|
if not self._full_name_is_definitive:
|
|
if self.outer_class is None:
|
|
if self._module.cpp_namespace_prefix:
|
|
if self._module.cpp_namespace_prefix == '::':
|
|
self.full_name = '::' + self.name
|
|
else:
|
|
self.full_name = self._module.cpp_namespace_prefix + '::' + self.name
|
|
else:
|
|
self.full_name = self.name
|
|
else:
|
|
self.full_name = '::'.join([self.outer_class.full_name, self.name])
|
|
|
|
def make_upper(s):
|
|
if s and s[0].islower():
|
|
return s[0].upper()+s[1:]
|
|
else:
|
|
return s
|
|
|
|
def flatten(name):
|
|
"make a name like::This look LikeThis"
|
|
return ''.join([make_upper(utils.mangle_name(s)) for s in name.split('::')])
|
|
|
|
self.mangled_name = flatten(self.name)
|
|
self.mangled_full_name = utils.mangle_name(self.full_name)
|
|
|
|
self._pystruct = "Py%s%s" % (prefix, self.mangled_full_name)
|
|
self.pytypestruct = "Py%s%s_Type" % (prefix, self.mangled_full_name)
|
|
|
|
self._iter_pystruct = "Py%s%sIter" % (prefix, self.mangled_full_name)
|
|
self.iter_pytypestruct = "Py%s%sIter_Type" % (prefix, self.mangled_full_name)
|
|
|
|
## re-register the class type handlers, now with class full name
|
|
self.register_alias(self.full_name)
|
|
|
|
self.python_to_c_converter = self.module.get_root().get_python_to_c_type_converter_function_name(
|
|
self.ThisContainerReturn(self.full_name))
|
|
|
|
|
|
def register_alias(self, alias):
|
|
"""Re-register the class with another base name, in addition to any
|
|
registrations that might have already been done."""
|
|
|
|
self.module.register_type(None, alias, self)
|
|
|
|
self.ThisContainerParameter.CTYPES.append(alias)
|
|
try:
|
|
param_type_matcher.register(alias, self.ThisContainerParameter)
|
|
except ValueError: pass
|
|
|
|
self.ThisContainerRefParameter.CTYPES.append(alias+'&')
|
|
try:
|
|
param_type_matcher.register(alias+'&', self.ThisContainerRefParameter)
|
|
except ValueError: pass
|
|
|
|
self.ThisContainerReturn.CTYPES.append(alias)
|
|
try:
|
|
return_type_matcher.register(alias, self.ThisContainerReturn)
|
|
except ValueError: pass
|
|
|
|
|
|
def generate_forward_declarations(self, code_sink, module):
|
|
"""
|
|
Generates forward declarations for the instance and type
|
|
structures.
|
|
"""
|
|
|
|
# container pystruct
|
|
code_sink.writeln('''
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
%s *obj;
|
|
} %s;
|
|
''' % (self.full_name, self.pystruct))
|
|
|
|
# container iterator pystruct
|
|
code_sink.writeln('''
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
%s *container;
|
|
%s::iterator *iterator;
|
|
} %s;
|
|
''' % (self.pystruct, self.full_name, self.iter_pystruct))
|
|
|
|
code_sink.writeln()
|
|
code_sink.writeln('extern PyTypeObject %s;' % (self.pytypestruct,))
|
|
code_sink.writeln('extern PyTypeObject %s;' % (self.iter_pytypestruct,))
|
|
code_sink.writeln()
|
|
|
|
this_type_converter = self.module.get_root().get_python_to_c_type_converter_function_name(
|
|
self.ThisContainerReturn(self.full_name))
|
|
self.module.get_root().declare_one_time_definition(this_type_converter)
|
|
code_sink.writeln('int %(CONTAINER_CONVERTER_FUNC_NAME)s(PyObject *arg, %(CTYPE)s *container);'
|
|
% {'CTYPE': self.full_name,
|
|
'CONTAINER_CONVERTER_FUNC_NAME': this_type_converter,
|
|
})
|
|
self.python_to_c_converter = this_type_converter
|
|
|
|
def _get_python_name(self):
|
|
if self.custom_name is None:
|
|
class_python_name = self.mangled_name
|
|
else:
|
|
class_python_name = self.custom_name
|
|
return class_python_name
|
|
|
|
python_name = property(_get_python_name)
|
|
|
|
def _get_python_full_name(self):
|
|
if self.outer_class is None:
|
|
mod_path = self._module.get_module_path()
|
|
mod_path.append(self.python_name)
|
|
return '.'.join(mod_path)
|
|
else:
|
|
return '%s.%s' % (self.outer_class.pytype.slots['tp_name'], self.python_name)
|
|
|
|
python_full_name = property(_get_python_full_name)
|
|
|
|
|
|
def generate(self, code_sink, module, docstring=None):
|
|
"""Generates the class to a code sink"""
|
|
|
|
## --- register the class type in the module ---
|
|
module.after_init.write_code("/* Register the '%s' class */" % self.full_name)
|
|
|
|
module.after_init.write_error_check('PyType_Ready(&%s)' % (self.pytypestruct,))
|
|
module.after_init.write_error_check('PyType_Ready(&%s)' % (self.iter_pytypestruct,))
|
|
|
|
class_python_name = self.python_name
|
|
|
|
if self.outer_class is None:
|
|
module.after_init.write_code(
|
|
'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) &%s);' % (
|
|
class_python_name, self.pytypestruct))
|
|
module.after_init.write_code(
|
|
'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) &%s);' % (
|
|
class_python_name+'Iter', self.iter_pytypestruct))
|
|
else:
|
|
module.after_init.write_code(
|
|
'PyDict_SetItemString((PyObject*) %s.tp_dict, (char *) \"%s\", (PyObject *) &%s);' % (
|
|
self.outer_class.pytypestruct, class_python_name, self.pytypestruct))
|
|
module.after_init.write_code(
|
|
'PyDict_SetItemString((PyObject*) %s.tp_dict, (char *) \"%s\", (PyObject *) &%s);' % (
|
|
self.outer_class.pytypestruct, class_python_name+'Iter', self.iter_pytypestruct))
|
|
|
|
self._generate_gc_methods(code_sink)
|
|
self._generate_destructor(code_sink)
|
|
self._generate_iter_methods(code_sink)
|
|
self._generate_container_constructor(code_sink)
|
|
self._generate_type_structure(code_sink, docstring)
|
|
|
|
def _generate_type_structure(self, code_sink, docstring):
|
|
"""generate the type structure"""
|
|
|
|
self.pytype.slots.setdefault("tp_basicsize", "sizeof(%s)" % (self.pystruct,))
|
|
self.pytype.slots.setdefault("tp_flags", "Py_TPFLAGS_DEFAULT")
|
|
self.pytype.slots.setdefault("typestruct", self.pytypestruct)
|
|
self.pytype.slots.setdefault("tp_name", self.python_full_name)
|
|
self.pytype.generate(code_sink)
|
|
|
|
self.iter_pytype.slots.setdefault("tp_basicsize", "sizeof(%s)" % (self.iter_pystruct,))
|
|
self.iter_pytype.slots.setdefault("tp_flags", ("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC"))
|
|
self.iter_pytype.slots.setdefault("typestruct", self.iter_pytypestruct)
|
|
self.iter_pytype.slots.setdefault("tp_name", self.python_full_name + 'Iter')
|
|
self.iter_pytype.generate(code_sink)
|
|
|
|
def _get_iter_delete_code(self):
|
|
delete_code = ("delete self->iterator;\n"
|
|
" self->iterator = NULL;\n")
|
|
return delete_code
|
|
|
|
def _get_container_delete_code(self):
|
|
delete_code = ("delete self->obj;\n"
|
|
" self->obj = NULL;\n")
|
|
return delete_code
|
|
|
|
def _generate_gc_methods(self, code_sink):
|
|
"""Generate tp_clear and tp_traverse"""
|
|
|
|
## --- iterator tp_clear ---
|
|
tp_clear_function_name = "%s__tp_clear" % (self.iter_pystruct,)
|
|
self.iter_pytype.slots.setdefault("tp_clear", tp_clear_function_name )
|
|
|
|
code_sink.writeln(r'''
|
|
static void
|
|
%s(%s *self)
|
|
{
|
|
Py_CLEAR(self->container);
|
|
%s
|
|
}
|
|
''' % (tp_clear_function_name, self.iter_pystruct, self._get_iter_delete_code()))
|
|
|
|
## --- iterator tp_traverse ---
|
|
tp_traverse_function_name = "%s__tp_traverse" % (self.iter_pystruct,)
|
|
self.iter_pytype.slots.setdefault("tp_traverse", tp_traverse_function_name )
|
|
|
|
code_sink.writeln(r'''
|
|
static int
|
|
%s(%s *self, visitproc visit, void *arg)
|
|
{
|
|
Py_VISIT((PyObject *) self->container);
|
|
return 0;
|
|
}
|
|
''' % (tp_traverse_function_name, self.iter_pystruct))
|
|
|
|
|
|
def _generate_destructor(self, code_sink):
|
|
"""Generate a tp_dealloc function and register it in the type"""
|
|
|
|
# -- container --
|
|
container_tp_dealloc_function_name = "_wrap_%s__tp_dealloc" % (self.pystruct,)
|
|
code_sink.writeln(r'''
|
|
static void
|
|
%s(%s *self)
|
|
{
|
|
%s
|
|
self->ob_type->tp_free((PyObject*)self);
|
|
}
|
|
''' % (container_tp_dealloc_function_name, self.pystruct,
|
|
self._get_container_delete_code()))
|
|
|
|
self.pytype.slots.setdefault("tp_dealloc", container_tp_dealloc_function_name )
|
|
|
|
|
|
# -- iterator --
|
|
iter_tp_dealloc_function_name = "_wrap_%s__tp_dealloc" % (self.iter_pystruct,)
|
|
code_sink.writeln(r'''
|
|
static void
|
|
%s(%s *self)
|
|
{
|
|
Py_CLEAR(self->container);
|
|
%s
|
|
self->ob_type->tp_free((PyObject*)self);
|
|
}
|
|
''' % (iter_tp_dealloc_function_name, self.iter_pystruct, self._get_iter_delete_code()))
|
|
|
|
self.iter_pytype.slots.setdefault("tp_dealloc", iter_tp_dealloc_function_name )
|
|
|
|
|
|
def _generate_iter_methods(self, code_sink):
|
|
|
|
container_tp_iter_function_name = "_wrap_%s__tp_iter" % (self.pystruct,)
|
|
iterator_tp_iter_function_name = "_wrap_%s__tp_iter" % (self.iter_pystruct,)
|
|
subst_vars = {
|
|
'CONTAINER_ITER_FUNC': container_tp_iter_function_name,
|
|
'ITERATOR_ITER_FUNC': iterator_tp_iter_function_name,
|
|
'PYSTRUCT': self.pystruct,
|
|
'ITER_PYSTRUCT': self.iter_pystruct,
|
|
'ITER_PYTYPESTRUCT': self.iter_pytypestruct,
|
|
'CTYPE': self.full_name,
|
|
}
|
|
# -- container --
|
|
code_sink.writeln(r'''
|
|
static PyObject*
|
|
%(CONTAINER_ITER_FUNC)s(%(PYSTRUCT)s *self)
|
|
{
|
|
%(ITER_PYSTRUCT)s *iter = PyObject_GC_New(%(ITER_PYSTRUCT)s, &%(ITER_PYTYPESTRUCT)s);
|
|
Py_INCREF(self);
|
|
iter->container = self;
|
|
iter->iterator = new %(CTYPE)s::iterator(self->obj->begin());
|
|
return (PyObject*) iter;
|
|
}
|
|
''' % subst_vars)
|
|
|
|
self.pytype.slots.setdefault("tp_iter", container_tp_iter_function_name)
|
|
|
|
|
|
# -- iterator --
|
|
container_tp_iter_function_name = "_wrap_%s__tp_iter" % (self.pystruct,)
|
|
code_sink.writeln(r'''
|
|
static PyObject*
|
|
%(ITERATOR_ITER_FUNC)s(%(ITER_PYSTRUCT)s *self)
|
|
{
|
|
Py_INCREF(self);
|
|
return (PyObject*) self;
|
|
}
|
|
''' % subst_vars)
|
|
|
|
self.iter_pytype.slots.setdefault("tp_iter", iterator_tp_iter_function_name)
|
|
|
|
# -- iterator tp_iternext
|
|
iternext = IterNextWrapper(self)
|
|
iternext.generate(code_sink)
|
|
self.iter_pytype.slots.setdefault("tp_iternext", iternext.c_function_name)
|
|
|
|
|
|
|
|
def _generate_container_constructor(self, code_sink):
|
|
container_tp_init_function_name = "_wrap_%s__tp_init" % (self.pystruct,)
|
|
item_python_to_c_converter = self.module.get_root().generate_python_to_c_type_converter(self.value_type, code_sink)
|
|
if self.key_type is not None:
|
|
key_python_to_c_converter = self.module.get_root().generate_python_to_c_type_converter(self.key_type, code_sink)
|
|
this_type_converter = self.module.get_root().get_python_to_c_type_converter_function_name(
|
|
self.ThisContainerReturn(self.full_name))
|
|
subst_vars = {
|
|
'FUNC': container_tp_init_function_name,
|
|
'PYSTRUCT': self.pystruct,
|
|
'PYTYPESTRUCT': self.pytypestruct,
|
|
'CTYPE': self.full_name,
|
|
'ITEM_CONVERTER': item_python_to_c_converter,
|
|
'PYTHON_NAME': self.python_name,
|
|
'ITEM_CTYPE': self.value_type.ctype,
|
|
'CONTAINER_CONVERTER_FUNC_NAME': this_type_converter,
|
|
'ADD_VALUE': self.container_traits.add_value_method,
|
|
}
|
|
|
|
if self.key_type is None:
|
|
|
|
# generate mapping converter function
|
|
|
|
code_sink.writeln(r'''
|
|
int %(CONTAINER_CONVERTER_FUNC_NAME)s(PyObject *arg, %(CTYPE)s *container)
|
|
{
|
|
if (PyObject_IsInstance(arg, (PyObject*) &%(PYTYPESTRUCT)s)) {
|
|
*container = *((%(PYSTRUCT)s*)arg)->obj;
|
|
} else if (PyList_Check(arg)) {
|
|
container->clear();
|
|
Py_ssize_t size = PyList_Size(arg);
|
|
for (Py_ssize_t i = 0; i < size; i++) {
|
|
%(ITEM_CTYPE)s item;
|
|
if (!%(ITEM_CONVERTER)s(PyList_GET_ITEM(arg, i), &item)) {
|
|
return 0;
|
|
}
|
|
container->%(ADD_VALUE)s(item);
|
|
}
|
|
} else {
|
|
PyErr_SetString(PyExc_TypeError, "parameter must be None, a %(PYTHON_NAME)s instance, or a list of %(ITEM_CTYPE)s");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
''' % subst_vars)
|
|
|
|
else:
|
|
|
|
# generate mapping converter function
|
|
|
|
subst_vars.update({
|
|
'KEY_CONVERTER': key_python_to_c_converter,
|
|
'KEY_CTYPE': self.key_type.ctype,
|
|
})
|
|
|
|
code_sink.writeln(r'''
|
|
int %(CONTAINER_CONVERTER_FUNC_NAME)s(PyObject *arg, %(CTYPE)s *container)
|
|
{
|
|
if (PyObject_IsInstance(arg, (PyObject*) &%(PYTYPESTRUCT)s)) {
|
|
*container = *((%(PYSTRUCT)s*)arg)->obj;
|
|
} else if (PyList_Check(arg)) {
|
|
container->clear();
|
|
Py_ssize_t size = PyList_Size(arg);
|
|
for (Py_ssize_t i = 0; i < size; i++) {
|
|
PyObject *tup = PyList_GET_ITEM(arg, i);
|
|
if (!PyTuple_Check(tup) || PyTuple_Size(tup) != 2) {
|
|
PyErr_SetString(PyExc_TypeError, "items must be tuples with two elements");
|
|
return 0;
|
|
}
|
|
std::pair<%(KEY_CTYPE)s, %(ITEM_CTYPE)s> item;
|
|
if (!%(KEY_CONVERTER)s(PyTuple_GET_ITEM(tup, 0), &item.first)) {
|
|
return 0;
|
|
}
|
|
if (!%(ITEM_CONVERTER)s(PyTuple_GET_ITEM(tup, 1), &item.second)) {
|
|
return 0;
|
|
}
|
|
container->%(ADD_VALUE)s(item);
|
|
}
|
|
} else {
|
|
PyErr_SetString(PyExc_TypeError, "parameter must be None, a %(PYTHON_NAME)s instance, or a list of %(ITEM_CTYPE)s");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
''' % subst_vars)
|
|
# ---
|
|
|
|
# constructor, calling the above converter function
|
|
code_sink.writeln(r'''
|
|
static int
|
|
%(FUNC)s(%(PYSTRUCT)s *self, PyObject *args, PyObject *kwargs)
|
|
{
|
|
const char *keywords[] = {"arg", NULL};
|
|
PyObject *arg = NULL;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "|O", (char **) keywords, &arg)) {
|
|
return -1;
|
|
}
|
|
|
|
self->obj = new %(CTYPE)s;
|
|
|
|
if (arg == NULL)
|
|
return 0;
|
|
|
|
if (!%(CONTAINER_CONVERTER_FUNC_NAME)s(arg, self->obj)) {
|
|
delete self->obj;
|
|
self->obj = NULL;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
''' % subst_vars)
|
|
|
|
self.pytype.slots.setdefault("tp_init", container_tp_init_function_name)
|
|
|
|
|
|
|
|
## ----------------------------
|
|
## Type Handlers
|
|
## ----------------------------
|
|
|
|
def _get_dummy_container():
|
|
return Container('dummy', ReturnValue.new('void'), 'list') # Container instance
|
|
|
|
class ContainerParameterBase(Parameter):
|
|
"Base class for all C++ Class parameter handlers"
|
|
CTYPES = []
|
|
container_type = _get_dummy_container()
|
|
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.container_type.name:
|
|
ctype = self.container_type.full_name
|
|
super(ContainerParameterBase, self).__init__(
|
|
ctype, name, direction, is_const, default_value)
|
|
|
|
## name of the PyFoo * variable used in parameter parsing
|
|
self.py_name = None
|
|
|
|
|
|
class ContainerReturnValueBase(ReturnValue):
|
|
"Class return handlers -- base class"
|
|
CTYPES = []
|
|
container_type = _get_dummy_container()
|
|
|
|
def __init__(self, ctype):
|
|
super(ContainerReturnValueBase, self).__init__(ctype)
|
|
## name of the PyFoo * variable used in return value building
|
|
self.py_name = None
|
|
|
|
|
|
|
|
|
|
class ContainerParameter(ContainerParameterBase):
|
|
"Container handlers"
|
|
CTYPES = []
|
|
container_type = _get_dummy_container()
|
|
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.container_type, Container)
|
|
|
|
assert self.default_value is None, "default value not implemented for containers"
|
|
|
|
#self.py_name = wrapper.declarations.declare_variable('PyObject*', self.name)
|
|
container_tmp_var = wrapper.declarations.declare_variable(
|
|
self.container_type.full_name, self.name + '_value')
|
|
wrapper.parse_params.add_parameter('O&', [self.container_type.python_to_c_converter, '&'+container_tmp_var], self.name)
|
|
wrapper.call_params.append(container_tmp_var)
|
|
|
|
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.container_type.pystruct+'*', 'py_'+self.container_type.name)
|
|
wrapper.before_call.write_code(
|
|
"%s = PyObject_New(%s, %s);" %
|
|
(self.py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct))
|
|
|
|
wrapper.before_call.write_code("%s->obj = new %s(%s);" % (self.py_name, self.container_type.full_name, self.value))
|
|
|
|
wrapper.build_params.add_parameter("N", [self.py_name])
|
|
|
|
|
|
class ContainerRefParameter(ContainerParameterBase):
|
|
"Container handlers"
|
|
CTYPES = []
|
|
container_type = _get_dummy_container()
|
|
DIRECTIONS = [Parameter.DIRECTION_IN, Parameter.DIRECTION_OUT, Parameter.DIRECTION_INOUT]
|
|
|
|
def convert_python_to_c(self, wrapper):
|
|
"parses python args to get C++ value"
|
|
assert isinstance(wrapper, ForwardWrapperBase)
|
|
assert isinstance(self.container_type, Container)
|
|
|
|
assert self.default_value is None, "default value not implemented for containers"
|
|
|
|
#self.py_name = wrapper.declarations.declare_variable('PyObject*', self.name)
|
|
container_tmp_var = wrapper.declarations.declare_variable(
|
|
self.container_type.full_name, self.name + '_value')
|
|
|
|
if self.direction & Parameter.DIRECTION_IN:
|
|
wrapper.parse_params.add_parameter('O&', [self.container_type.python_to_c_converter, '&'+container_tmp_var], self.name)
|
|
|
|
wrapper.call_params.append(container_tmp_var)
|
|
|
|
if self.direction & Parameter.DIRECTION_OUT:
|
|
py_name = wrapper.declarations.declare_variable(
|
|
self.container_type.pystruct+'*', 'py_'+self.container_type.name)
|
|
wrapper.after_call.write_code(
|
|
"%s = PyObject_New(%s, %s);" %
|
|
(py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct))
|
|
wrapper.after_call.write_code("%s->obj = new %s(%s);" % (py_name, self.container_type.full_name, container_tmp_var))
|
|
wrapper.build_params.add_parameter("N", [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.container_type.pystruct+'*', 'py_'+self.container_type.name)
|
|
wrapper.before_call.write_code(
|
|
"%s = PyObject_New(%s, %s);" %
|
|
(self.py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct))
|
|
|
|
if self.direction & Parameter.DIRECTION_IN:
|
|
wrapper.before_call.write_code("%s->obj = new %s(%s);" % (self.py_name, self.container_type.full_name, self.name))
|
|
else:
|
|
wrapper.before_call.write_code("%s->obj = new %s;" % (self.py_name, self.container_type.full_name))
|
|
|
|
wrapper.build_params.add_parameter("N", [self.py_name])
|
|
|
|
if self.direction & Parameter.DIRECTION_OUT:
|
|
wrapper.parse_params.add_parameter('O&', [self.container_type.python_to_c_converter, '&'+self.name], self.name)
|
|
|
|
|
|
class ContainerPtrParameter(ContainerParameterBase):
|
|
"Container handlers"
|
|
CTYPES = []
|
|
container_type = _get_dummy_container()
|
|
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, transfer_ownership=None):
|
|
super(ContainerPtrParameter, self).__init__(ctype, name, direction, is_const, default_value)
|
|
|
|
if self.direction == Parameter.DIRECTION_OUT:
|
|
if transfer_ownership is not None and not transfer_ownership:
|
|
raise TypeConfigurationError("with direction=out, transfer_ownership must be True or omitted (got %r)"
|
|
% transfer_ownership)
|
|
self.transfer_ownership = True
|
|
else:
|
|
if transfer_ownership is None:
|
|
raise TypeConfigurationError("transfer_ownership parameter was not given")
|
|
self.transfer_ownership = transfer_ownership
|
|
|
|
def convert_python_to_c(self, wrapper):
|
|
"parses python args to get C++ value"
|
|
assert isinstance(wrapper, ForwardWrapperBase)
|
|
assert isinstance(self.container_type, Container)
|
|
|
|
assert self.default_value is None, "default value not implemented for containers"
|
|
|
|
|
|
if self.direction == Parameter.DIRECTION_IN:
|
|
container_tmp_var = wrapper.declarations.declare_variable(
|
|
self.container_type.full_name, self.name + '_value')
|
|
wrapper.parse_params.add_parameter('O&', [self.container_type.python_to_c_converter, '&'+container_tmp_var], self.name)
|
|
if self.transfer_ownership:
|
|
wrapper.call_params.append("new %s(%s)" % (self.container_type.full_name, container_tmp_var))
|
|
else:
|
|
wrapper.call_params.append("&%s" % container_tmp_var)
|
|
|
|
elif self.direction == Parameter.DIRECTION_OUT:
|
|
container_tmp_var = wrapper.declarations.declare_variable(
|
|
self.container_type.full_name + '*', self.name + '_value', initializer="new %s" % self.container_type.full_name)
|
|
|
|
wrapper.call_params.append(container_tmp_var)
|
|
|
|
py_name = wrapper.declarations.declare_variable(
|
|
self.container_type.pystruct+'*', 'py_'+self.container_type.name)
|
|
|
|
wrapper.after_call.write_code(
|
|
"%s = PyObject_New(%s, %s);" %
|
|
(py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct))
|
|
|
|
wrapper.after_call.write_code("%s->obj = %s;" % (py_name, container_tmp_var))
|
|
|
|
wrapper.build_params.add_parameter("N", [py_name])
|
|
|
|
else:
|
|
raise NotSupportedError("inout not supported for container*")
|
|
|
|
|
|
def convert_c_to_python(self, wrapper):
|
|
'''Write some code before calling the Python method.'''
|
|
raise NotSupportedError("container* reverse type handler not yet implemeneted")
|
|
|
|
|
|
class ContainerReturnValue(ContainerReturnValueBase):
|
|
"Container type return handlers"
|
|
CTYPES = []
|
|
container_type = _get_dummy_container()
|
|
|
|
def __init__(self, ctype, is_const=False):
|
|
"""override to fix the ctype parameter with namespace information"""
|
|
if ctype == self.container_type.name:
|
|
ctype = self.container_type.full_name
|
|
super(ContainerReturnValue, self).__init__(ctype)
|
|
self.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.container_type.full_name,)
|
|
|
|
def convert_c_to_python(self, wrapper):
|
|
"""see ReturnValue.convert_c_to_python"""
|
|
py_name = wrapper.declarations.declare_variable(
|
|
self.container_type.pystruct+'*', 'py_'+self.container_type.name)
|
|
self.py_name = py_name
|
|
wrapper.after_call.write_code(
|
|
"%s = PyObject_New(%s, %s);" %
|
|
(py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct))
|
|
wrapper.after_call.write_code("%s->obj = new %s(%s);" % (self.py_name, self.container_type.full_name, self.value))
|
|
wrapper.build_params.add_parameter("N", [py_name], prepend=True)
|
|
|
|
def convert_python_to_c(self, wrapper):
|
|
"""see ReturnValue.convert_python_to_c"""
|
|
wrapper.parse_params.add_parameter('O&', [self.container_type.python_to_c_converter, '&'+self.value])
|
|
|
|
|
|
## end
|