From bbfbe4702e44ab210842dd38f9bfe97038d36878 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 4 Jan 2008 18:22:57 +0000 Subject: [PATCH] Add general support for templated classes (previously only classes with non-templated typedefs were allowed). --- pybindgen/cppclass.py | 40 ++++++++++++++++---- pybindgen/gccxmlparser.py | 79 ++++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 28 deletions(-) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 4b10360..2b19de2 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -33,7 +33,7 @@ class CppHelperClass(object): class_ -- original CppClass wrapper object """ self.class_ = class_ - self.name = class_.name + "_PythonProxy" + self.name = class_.pystruct + "__PythonHelper" self.virtual_parent_callers = {} self.virtual_proxies = [] self.cannot_be_constructed = False @@ -95,7 +95,7 @@ class CppHelperClass(object): code_sink.writeln("%s(%s)" % (self.name, ', '.join(params))) code_sink.indent() code_sink.writeln(": %s(%s), m_pyself(NULL)\n{}" % - (self.class_.name, + (self.class_.full_name, ', '.join([param.name for param in cons.parameters]))) code_sink.unindent() code_sink.writeln() @@ -261,7 +261,8 @@ class CppClass(object): def __init__(self, name, parent=None, incref_method=None, decref_method=None, automatic_type_narrowing=None, allow_subclassing=None, is_singleton=False, outer_class=None, - peekref_method=None): + peekref_method=None, + template_parameters=(), custom_template_class_name=None): """Constructor name -- class name parent -- optional parent class wrapper @@ -290,8 +291,10 @@ class CppClass(object): self.outer_class = outer_class self._module = None self.name = name + self.template_parameters = template_parameters + self.custom_template_class_name = custom_template_class_name self.is_singleton = is_singleton - self.full_name = None # full name with C++ namespaces attached + self.full_name = None # full name with C++ namespaces attached and template parameters self.methods = {} # name => OverloadedMethod self.constructors = [] # (name, wrapper) pairs self.slots = dict() @@ -478,7 +481,21 @@ class CppClass(object): else: return s - flat_name = ''.join([make_upper(s) for s in self.full_name.split('::')]) + def mangle(s): + "make a name Like look Like__lt__This_and_That__gt__" + s = s.replace('<', '__lt__').replace('>', '__gt__').replace(',', '_') + s = s.replace(' ', '_').replace('&', '__amp__').replace('*', '__star__') + return s + + def flatten(name): + "make a name like::This look LikeThis" + return ''.join([make_upper(mangle(s)) for s in name.split('::')]) + + flat_name = flatten(self.full_name) + + if self.template_parameters: + self.full_name += "< %s >" % (', '.join(self.template_parameters)) + flat_name += '__' + '_'.join([flatten(s) for s in self.template_parameters]) self._pystruct = "Py%s%s" % (prefix, flat_name) self.metaclass_name = "%sMeta" % flat_name @@ -818,14 +835,23 @@ typedef struct { module.after_init.write_error_check('PyType_Ready(&%s)' % (self.pytypestruct,)) + + if self.template_parameters: + if self.custom_template_class_name is None: + class_python_name = utils.get_mangled_name(self.name, self.template_parameters) + else: + class_python_name = self.custom_template_class_name + else: + class_python_name = self.name + if self.outer_class is None: module.after_init.write_code( 'PyModule_AddObject(m, \"%s\", (PyObject *) &%s);' % ( - self.name, self.pytypestruct)) + class_python_name, self.pytypestruct)) else: module.after_init.write_code( 'PyDict_SetItemString((PyObject*) %s.tp_dict, \"%s\", (PyObject *) &%s);' % ( - self.outer_class.pytypestruct, self.name, self.pytypestruct)) + self.outer_class.pytypestruct, class_python_name, self.pytypestruct)) have_constructor = self._generate_constructor(code_sink) diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index e2861fc..e0701e9 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -49,6 +49,21 @@ class ErrorHandler(settings.ErrorHandler): return True settings.error_handler = ErrorHandler() +_digits = re.compile(r"\s*\d+\s*") +def normalize_name(decl_string): + if templates.is_instantiation(decl_string): + cls_name, template_parameters = templates.split(decl_string) + else: + cls_name = decl_string + template_parameters = None + if not _digits.match(cls_name): + if not cls_name.startswith('::'): + cls_name = '::' + cls_name + if template_parameters is None: + return cls_name + else: + return "%s< %s >" % (cls_name, ', '.join([normalize_name(name) for name in template_parameters])) + class GccXmlTypeRegistry(object): def __init__(self): @@ -57,19 +72,23 @@ class GccXmlTypeRegistry(object): def register_class(self, cpp_class, alias=None): assert isinstance(cpp_class, CppClass) - if cpp_class.full_name.startswith('::'): - full_name = cpp_class.full_name - else: - full_name = '::' + cpp_class.full_name + full_name = normalize_name(cpp_class.full_name) + #print >> sys.stderr, "******** registering class %s as %s" \ + # % (cpp_class.gccxml_definition, full_name) self.classes[full_name] = cpp_class if alias is not None: + alias = normalize_name(alias) self.classes[alias] = cpp_class + #print >> sys.stderr, "******** registering class %s also as alias %s" \ + # % (cpp_class.gccxml_definition, alias) def find_class(self, class_name, module_namespace): if not class_name.startswith(module_namespace): class_name = module_namespace + class_name - if not class_name.startswith('::'): - class_name = '::' + class_name + class_name = normalize_name(class_name) + #if '<' in class_name: + # print >> sys.stderr, "******** looking for class %s" \ + # % (class_name) return self.classes[class_name] def get_class_type_traits(self, type_info): @@ -83,8 +102,12 @@ class GccXmlTypeRegistry(object): is_pointer = False pointer_is_const = False if isinstance(base_type, cpptypes.declarated_t): + class_name = normalize_name(base_type.decl_string) + #if '<' in class_name: + # print >> sys.stderr, "******** looking for class %s" \ + # % (class_name) try: - cpp_class = self.classes[base_type.decl_string] + cpp_class = self.classes[class_name] except KeyError: return (None, is_const, is_pointer, is_reference, pointer_is_const) @@ -427,10 +450,7 @@ class ModuleParser(object): if typedef_type == cls: break else: - warnings.warn_explicit("Class %s ignored because it is templated; templates not yet supported" - % cls.decl_string, - Warning, cls.location.file_name, cls.location.line) - continue + typedef = None if len(cls.bases) > 1: warnings.warn_explicit(("Class %s ignored because it uses multiple " @@ -494,19 +514,42 @@ class ModuleParser(object): if not self._class_has_public_destructor(cls): kwargs.setdefault('is_singleton', True) + custom_template_class_name = None + template_parameters = () if typedef is None: - name = cls.name alias = None + if templates.is_instantiation(cls.decl_string): + cls_name, template_parameters = templates.split(cls.name) + assert template_parameters + a1 = 1 + if '::' in cls_name: + cls_name = cls_name.split('::')[-1] + template_instance_names = global_annotations.get('template_instance_names', '') + if template_instance_names: + for mapping in template_instance_names.split('|'): + type_names, name = mapping.split('=>') + instance_types = type_names.split(',') + if instance_types == template_parameters: + custom_template_class_name = name + break + else: + a2 = 1 + cls_name = cls.name else: - name = typedef.name + cls_name = typedef.name alias = '::'.join([module.cpp_namespace_prefix, cls.name]) - #print >> sys.stderr, "******** registering class %s" % cls - class_wrapper = CppClass(name, parent=base_class_wrapper, outer_class=outer_class, **kwargs) + + class_wrapper = CppClass(cls_name, parent=base_class_wrapper, outer_class=outer_class, + template_parameters=template_parameters, + custom_template_class_name=custom_template_class_name, + **kwargs) class_wrapper.gccxml_definition = cls module.add_class(class_wrapper) registered_classes[cls] = class_wrapper type_registry.register_class(class_wrapper, alias) + del cls_name + ## scan for nested classes/enums self._scan_namespace_types(module, module_namespace, outer_class=class_wrapper) @@ -514,12 +557,6 @@ class ModuleParser(object): other_class = type_registry.find_class(operator.return_type.decl_string, '::') class_wrapper.implicitly_converts_to(other_class) - assert typedef is not None or \ - (cls.decl_string in type_registry.classes\ - and type_registry.classes[cls.decl_string] == class_wrapper) - - - for cls, class_wrapper in registered_classes.iteritems(): self._scan_methods(cls, class_wrapper)