Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
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)