271 lines
9.9 KiB
Python
271 lines
9.9 KiB
Python
"""
|
|
Add container iteration powers to wrapped C++ classes
|
|
"""
|
|
|
|
from typehandlers.base import ForwardWrapperBase
|
|
from typehandlers import codesink
|
|
from pytypeobject import PyTypeObject
|
|
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, CppClassContainerTraits)
|
|
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()
|
|
|
|
def reset_code_generation_state(self):
|
|
super(IterNextWrapper, self).reset_code_generation_state()
|
|
self.iter_variable_name = self.declarations.declare_variable(
|
|
"%s::%s" % (self.container.cppclass.full_name, self.container.iterator_type), '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->%s()" % (self.iter_variable_name, self.container.end_method),
|
|
"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 CppClassContainerTraits(object):
|
|
def __init__(self, cppclass, value_type, begin_method='begin', end_method='end', iterator_type='iterator', is_mapping=False):
|
|
"""
|
|
:param cppclass: the L{CppClass} object that receives the container traits
|
|
|
|
:param value_type: a ReturnValue of the element type: note,
|
|
for mapping containers, value_type is a tuple with two
|
|
ReturnValue's: (key, element).
|
|
"""
|
|
self.cppclass = cppclass
|
|
self.begin_method = begin_method
|
|
self.end_method = end_method
|
|
self.iterator_type = iterator_type
|
|
|
|
self.iter_pytype = PyTypeObject()
|
|
self._iter_pystruct = None
|
|
|
|
if 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)
|
|
|
|
def get_iter_pystruct(self):
|
|
return "%s_Iter" % self.cppclass.pystruct
|
|
iter_pystruct = property(get_iter_pystruct)
|
|
|
|
def get_iter_pytypestruct(self):
|
|
return "%s_IterType" % self.cppclass.pystruct
|
|
iter_pytypestruct = property(get_iter_pytypestruct)
|
|
|
|
|
|
def generate_forward_declarations(self, code_sink, dummy_module):
|
|
"""
|
|
Generates forward declarations for the instance and type
|
|
structures.
|
|
"""
|
|
|
|
# container iterator pystruct
|
|
code_sink.writeln('''
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
%s *container;
|
|
%s::%s *iterator;
|
|
} %s;
|
|
''' % (self.cppclass.pystruct, self.cppclass.full_name, self.iterator_type, self.iter_pystruct))
|
|
|
|
code_sink.writeln()
|
|
code_sink.writeln('extern PyTypeObject %s;' % (self.iter_pytypestruct,))
|
|
code_sink.writeln()
|
|
|
|
def get_iter_python_name(self):
|
|
return "%sIter" % self.cppclass.get_python_name()
|
|
|
|
def get_iter_python_full_name(self, module):
|
|
if self.cppclass.outer_class is None:
|
|
mod_path = module.get_module_path()
|
|
mod_path.append(self.get_iter_python_name())
|
|
return '.'.join(mod_path)
|
|
else:
|
|
return '%s.%s' % (self.cppclass.outer_class.pytype.slots['tp_name'],
|
|
self.get_iter_python_name())
|
|
|
|
|
|
def generate(self, code_sink, module, docstring=None):
|
|
"""Generates the class to a code sink"""
|
|
|
|
## --- register the iter type in the module ---
|
|
module.after_init.write_code("/* Register the '%s' class iterator*/" % self.cppclass.full_name)
|
|
module.after_init.write_error_check('PyType_Ready(&%s)' % (self.iter_pytypestruct,))
|
|
|
|
if self.cppclass.outer_class is None:
|
|
module.after_init.write_code(
|
|
'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) &%s);' % (
|
|
self.get_iter_python_name(), self.iter_pytypestruct))
|
|
else:
|
|
module.after_init.write_code(
|
|
'PyDict_SetItemString((PyObject*) %s.tp_dict, (char *) \"%s\", (PyObject *) &%s);' % (
|
|
self.cppclass.outer_class.pytypestruct, self.cppclass.get_iter_python_name(), self.iter_pytypestruct))
|
|
|
|
self._generate_gc_methods(code_sink)
|
|
self._generate_destructor(code_sink)
|
|
self._generate_iter_methods(code_sink)
|
|
self._generate_type_structure(code_sink, module, docstring)
|
|
|
|
def _generate_type_structure(self, code_sink, module, docstring):
|
|
"""generate the type structure"""
|
|
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.get_iter_python_full_name(module))
|
|
if docstring:
|
|
self.iter_pytype.slots.setdefault("tp_doc", '"%s"' % docstring)
|
|
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"""
|
|
|
|
# -- 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.cppclass.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.cppclass.pystruct,
|
|
'ITER_PYSTRUCT': self.iter_pystruct,
|
|
'ITER_PYTYPESTRUCT': self.iter_pytypestruct,
|
|
'CTYPE': self.cppclass.full_name,
|
|
'BEGIN_METHOD': self.begin_method,
|
|
'ITERATOR_TYPE': self.iterator_type,
|
|
}
|
|
# -- 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_TYPE)s(self->obj->%(BEGIN_METHOD)s());
|
|
return (PyObject*) iter;
|
|
}
|
|
''' % subst_vars)
|
|
|
|
self.cppclass.pytype.slots.setdefault("tp_iter", container_tp_iter_function_name)
|
|
|
|
|
|
# -- iterator --
|
|
container_tp_iter_function_name = "_wrap_%s__tp_iter" % (self.cppclass.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)
|
|
|
|
|