summaryrefslogtreecommitdiff
path: root/libs/python/pyste/src/Pyste/ClassExporter.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/python/pyste/src/Pyste/ClassExporter.py')
-rw-r--r--libs/python/pyste/src/Pyste/ClassExporter.py918
1 files changed, 918 insertions, 0 deletions
diff --git a/libs/python/pyste/src/Pyste/ClassExporter.py b/libs/python/pyste/src/Pyste/ClassExporter.py
new file mode 100644
index 000000000..decaf628e
--- /dev/null
+++ b/libs/python/pyste/src/Pyste/ClassExporter.py
@@ -0,0 +1,918 @@
+# Copyright Bruno da Silva de Oliveira 2003. Use, modification and
+# distribution is subject to the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+import exporters
+from Exporter import Exporter
+from declarations import *
+from settings import *
+from policies import *
+from SingleCodeUnit import SingleCodeUnit
+from EnumExporter import EnumExporter
+from utils import makeid, enumerate
+import copy
+import exporterutils
+import re
+
+#==============================================================================
+# ClassExporter
+#==============================================================================
+class ClassExporter(Exporter):
+ 'Generates boost.python code to export a class declaration'
+
+ def __init__(self, info, parser_tail=None):
+ Exporter.__init__(self, info, parser_tail)
+ # sections of code
+ self.sections = {}
+ # template: each item in the list is an item into the class_<...>
+ # section.
+ self.sections['template'] = []
+ # constructor: each item in the list is a parameter to the class_
+ # constructor, like class_<C>(...)
+ self.sections['constructor'] = []
+ # inside: everything within the class_<> statement
+ self.sections['inside'] = []
+ # scope: items outside the class statement but within its scope.
+ # scope* s = new scope(class<>());
+ # ...
+ # delete s;
+ self.sections['scope'] = []
+ # declarations: outside the BOOST_PYTHON_MODULE macro
+ self.sections['declaration'] = []
+ self.sections['declaration-outside'] = []
+ self.sections['include'] = []
+ # a list of Constructor instances
+ self.constructors = []
+ # a list of code units, generated by nested declarations
+ self.nested_codeunits = []
+
+
+ def ScopeName(self):
+ return makeid(self.class_.FullName()) + '_scope'
+
+
+ def Name(self):
+ return self.info.name
+
+
+ def SetDeclarations(self, declarations):
+ Exporter.SetDeclarations(self, declarations)
+ if self.declarations:
+ decl = self.GetDeclaration(self.info.name)
+ if isinstance(decl, Typedef):
+ self.class_ = self.GetDeclaration(decl.type.name)
+ if not self.info.rename:
+ self.info.rename = decl.name
+ else:
+ self.class_ = decl
+ self.class_ = copy.deepcopy(self.class_)
+ else:
+ self.class_ = None
+
+
+ def ClassBases(self):
+ all_bases = []
+ for level in self.class_.hierarchy:
+ for base in level:
+ all_bases.append(base)
+ return [self.GetDeclaration(x.name) for x in all_bases]
+
+
+ def Order(self):
+ '''Return the TOTAL number of bases that this class has, including the
+ bases' bases. Do this because base classes must be instantialized
+ before the derived classes in the module definition.
+ '''
+ num_bases = len(self.ClassBases())
+ return num_bases, self.class_.FullName()
+
+
+ def Export(self, codeunit, exported_names):
+ self.InheritMethods(exported_names)
+ self.MakeNonVirtual()
+ if not self.info.exclude:
+ self.ExportBasics()
+ self.ExportBases(exported_names)
+ self.ExportConstructors()
+ self.ExportVariables()
+ self.ExportVirtualMethods(codeunit)
+ self.ExportMethods()
+ self.ExportOperators()
+ self.ExportNestedClasses(exported_names)
+ self.ExportNestedEnums(exported_names)
+ self.ExportSmartPointer()
+ self.ExportOpaquePointerPolicies()
+ self.ExportAddedCode()
+ self.Write(codeunit)
+ exported_names[self.Name()] = 1
+
+
+ def InheritMethods(self, exported_names):
+ '''Go up in the class hierarchy looking for classes that were not
+ exported yet, and then add their public members to this classes
+ members, as if they were members of this class. This allows the user to
+ just export one type and automatically get all the members from the
+ base classes.
+ '''
+ valid_members = (Method, ClassVariable, NestedClass, ClassEnumeration)
+ fullnames = [x.FullName() for x in self.class_]
+ pointers = [x.PointerDeclaration(True) for x in self.class_ if isinstance(x, Method)]
+ fullnames = dict([(x, None) for x in fullnames])
+ pointers = dict([(x, None) for x in pointers])
+ for level in self.class_.hierarchy:
+ level_exported = False
+ for base in level:
+ base = self.GetDeclaration(base.name)
+ if base.FullName() not in exported_names:
+ for member in base:
+ if type(member) in valid_members:
+ member_copy = copy.deepcopy(member)
+ member_copy.class_ = self.class_.FullName()
+ if isinstance(member_copy, Method):
+ pointer = member_copy.PointerDeclaration(True)
+ if pointer not in pointers:
+ self.class_.AddMember(member)
+ pointers[pointer] = None
+ elif member_copy.FullName() not in fullnames:
+ self.class_.AddMember(member)
+ else:
+ level_exported = True
+ if level_exported:
+ break
+ def IsValid(member):
+ return isinstance(member, valid_members) and member.visibility == Scope.public
+ self.public_members = [x for x in self.class_ if IsValid(x)]
+
+
+ def Write(self, codeunit):
+ indent = self.INDENT
+ boost_ns = namespaces.python
+ pyste_ns = namespaces.pyste
+ code = ''
+ # begin a scope for this class if needed
+ nested_codeunits = self.nested_codeunits
+ needs_scope = self.sections['scope'] or nested_codeunits
+ if needs_scope:
+ scope_name = self.ScopeName()
+ code += indent + boost_ns + 'scope* %s = new %sscope(\n' %\
+ (scope_name, boost_ns)
+ # export the template section
+ template_params = ', '.join(self.sections['template'])
+ code += indent + boost_ns + 'class_< %s >' % template_params
+ # export the constructor section
+ constructor_params = ', '.join(self.sections['constructor'])
+ code += '(%s)\n' % constructor_params
+ # export the inside section
+ in_indent = indent*2
+ for line in self.sections['inside']:
+ code += in_indent + line + '\n'
+ # write the scope section and end it
+ if not needs_scope:
+ code += indent + ';\n'
+ else:
+ code += indent + ');\n'
+ for line in self.sections['scope']:
+ code += indent + line + '\n'
+ # write the contents of the nested classes
+ for nested_unit in nested_codeunits:
+ code += '\n' + nested_unit.Section('module')
+ # close the scope
+ code += indent + 'delete %s;\n' % scope_name
+
+ # write the code to the module section in the codeunit
+ codeunit.Write('module', code + '\n')
+
+ # write the declarations to the codeunit
+ declarations = '\n'.join(self.sections['declaration'])
+ for nested_unit in nested_codeunits:
+ declarations += nested_unit.Section('declaration')
+ if declarations:
+ codeunit.Write('declaration', declarations + '\n')
+ declarations_outside = '\n'.join(self.sections['declaration-outside'])
+ if declarations_outside:
+ codeunit.Write('declaration-outside', declarations_outside + '\n')
+
+ # write the includes to the codeunit
+ includes = '\n'.join(self.sections['include'])
+ for nested_unit in nested_codeunits:
+ includes += nested_unit.Section('include')
+ if includes:
+ codeunit.Write('include', includes)
+
+
+ def Add(self, section, item):
+ 'Add the item into the corresponding section'
+ self.sections[section].append(item)
+
+
+ def ExportBasics(self):
+ '''Export the name of the class and its class_ statement.'''
+ class_name = self.class_.FullName()
+ self.Add('template', class_name)
+ name = self.info.rename or self.class_.name
+ self.Add('constructor', '"%s"' % name)
+
+
+ def ExportBases(self, exported_names):
+ 'Expose the bases of the class into the template section'
+ hierarchy = self.class_.hierarchy
+ exported = []
+ for level in hierarchy:
+ for base in level:
+ if base.visibility == Scope.public and base.name in exported_names:
+ exported.append(base.name)
+ if exported:
+ break
+ if exported:
+ code = namespaces.python + 'bases< %s > ' % (', '.join(exported))
+ self.Add('template', code)
+
+
+ def ExportConstructors(self):
+ '''Exports all the public contructors of the class, plus indicates if the
+ class is noncopyable.
+ '''
+ py_ns = namespaces.python
+ indent = self.INDENT
+
+ def init_code(cons):
+ 'return the init<>() code for the given contructor'
+ param_list = [p.FullName() for p in cons.parameters]
+ min_params_list = param_list[:cons.minArgs]
+ max_params_list = param_list[cons.minArgs:]
+ min_params = ', '.join(min_params_list)
+ max_params = ', '.join(max_params_list)
+ init = py_ns + 'init< '
+ init += min_params
+ if max_params:
+ if min_params:
+ init += ', '
+ init += py_ns + ('optional< %s >' % max_params)
+ init += ' >()'
+ return init
+
+ constructors = [x for x in self.public_members if isinstance(x, Constructor)]
+ # don't export copy constructors if the class is abstract
+ # we could remove all constructors, but this will have the effect of
+ # inserting no_init in the declaration, which would not allow
+ # even subclasses to be instantiated.
+ self.constructors = constructors[:]
+ if self.class_.abstract:
+ for cons in constructors:
+ if cons.IsCopy():
+ constructors.remove(cons)
+ break
+
+ if not constructors:
+ # declare no_init
+ self.Add('constructor', py_ns + 'no_init')
+ else:
+ # write the constructor with less parameters to the constructor section
+ smaller = None
+ for cons in constructors:
+ if smaller is None or len(cons.parameters) < len(smaller.parameters):
+ smaller = cons
+ assert smaller is not None
+ self.Add('constructor', init_code(smaller))
+ constructors.remove(smaller)
+ # write the rest to the inside section, using def()
+ for cons in constructors:
+ code = '.def(%s)' % init_code(cons)
+ self.Add('inside', code)
+
+ # check if the class is copyable
+ if not self.class_.HasCopyConstructor() or self.class_.abstract:
+ self.Add('template', namespaces.boost + 'noncopyable')
+
+
+ def ExportVariables(self):
+ 'Export the variables of the class, both static and simple variables'
+ vars = [x for x in self.public_members if isinstance(x, Variable)]
+ for var in vars:
+ if self.info[var.name].exclude:
+ continue
+ name = self.info[var.name].rename or var.name
+ fullname = var.FullName()
+ if var.type.const:
+ def_ = '.def_readonly'
+ else:
+ def_ = '.def_readwrite'
+ code = '%s("%s", &%s)' % (def_, name, fullname)
+ self.Add('inside', code)
+
+
+ def OverloadName(self, method):
+ 'Returns the name of the overloads struct for the given method'
+ name = makeid(method.FullName())
+ overloads = '_overloads_%i_%i' % (method.minArgs, method.maxArgs)
+ return name + overloads
+
+
+ def GetAddedMethods(self):
+ added_methods = self.info.__added__
+ result = []
+ if added_methods:
+ for name, rename in added_methods:
+ decl = self.GetDeclaration(name)
+ self.info[name].rename = rename
+ result.append(decl)
+ return result
+
+
+ def ExportMethods(self):
+ '''Export all the non-virtual methods of this class, plus any function
+ that is to be exported as a method'''
+
+ declared = {}
+ def DeclareOverloads(m):
+ 'Declares the macro for the generation of the overloads'
+ if (isinstance(m, Method) and m.static) or type(m) == Function:
+ func = m.FullName()
+ macro = 'BOOST_PYTHON_FUNCTION_OVERLOADS'
+ else:
+ func = m.name
+ macro = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS'
+ code = '%s(%s, %s, %i, %i)\n' % (macro, self.OverloadName(m), func, m.minArgs, m.maxArgs)
+ if code not in declared:
+ declared[code] = True
+ self.Add('declaration', code)
+
+
+ def Pointer(m):
+ 'returns the correct pointer declaration for the method m'
+ # check if this method has a wrapper set for him
+ wrapper = self.info[m.name].wrapper
+ if wrapper:
+ return '&' + wrapper.FullName()
+ else:
+ return m.PointerDeclaration()
+
+ def IsExportable(m):
+ 'Returns true if the given method is exportable by this routine'
+ ignore = (Constructor, ClassOperator, Destructor)
+ return isinstance(m, Function) and not isinstance(m, ignore) and not m.virtual
+
+ methods = [x for x in self.public_members if IsExportable(x)]
+ methods.extend(self.GetAddedMethods())
+
+ staticmethods = {}
+
+ for method in methods:
+ method_info = self.info[method.name]
+
+ # skip this method if it was excluded by the user
+ if method_info.exclude:
+ continue
+
+ # rename the method if the user requested
+ name = method_info.rename or method.name
+
+ # warn the user if this method needs a policy and doesn't have one
+ method_info.policy = exporterutils.HandlePolicy(method, method_info.policy)
+
+ # check for policies
+ policy = method_info.policy or ''
+ if policy:
+ policy = ', %s%s()' % (namespaces.python, policy.Code())
+ # check for overloads
+ overload = ''
+ if method.minArgs != method.maxArgs and not method_info.wrapper:
+ # add the overloads for this method
+ DeclareOverloads(method)
+ overload_name = self.OverloadName(method)
+ overload = ', %s%s()' % (namespaces.pyste, overload_name)
+
+ # build the .def string to export the method
+ pointer = Pointer(method)
+ code = '.def("%s", %s' % (name, pointer)
+ code += policy
+ code += overload
+ code += ')'
+ self.Add('inside', code)
+ # static method
+ if isinstance(method, Method) and method.static:
+ staticmethods[name] = 1
+ # add wrapper code if this method has one
+ wrapper = method_info.wrapper
+ if wrapper and wrapper.code:
+ self.Add('declaration', wrapper.code)
+
+ # export staticmethod statements
+ for name in staticmethods:
+ code = '.staticmethod("%s")' % name
+ self.Add('inside', code)
+
+
+
+ def MakeNonVirtual(self):
+ '''Make all methods that the user indicated to no_override no more virtual, delegating their
+ export to the ExportMethods routine'''
+ for member in self.class_:
+ if type(member) == Method and member.virtual:
+ member.virtual = not self.info[member.name].no_override
+
+
+ def ExportVirtualMethods(self, codeunit):
+ # check if this class has any virtual methods
+ has_virtual_methods = False
+ for member in self.class_:
+ if type(member) == Method and member.virtual:
+ has_virtual_methods = True
+ break
+
+ holder = self.info.holder
+ if has_virtual_methods:
+ generator = _VirtualWrapperGenerator(self.class_, self.ClassBases(), self.info, codeunit)
+ if holder:
+ self.Add('template', holder(generator.FullName()))
+ else:
+ self.Add('template', generator.FullName())
+ for definition in generator.GenerateDefinitions():
+ self.Add('inside', definition)
+ self.Add('declaration', generator.GenerateVirtualWrapper(self.INDENT))
+ else:
+ if holder:
+ self.Add('template', holder(self.class_.FullName()))
+
+ # operators natively supported by boost
+ BOOST_SUPPORTED_OPERATORS = '+ - * / % ^ & ! ~ | < > == != <= >= << >> && || += -= '\
+ '*= /= %= ^= &= |= <<= >>='.split()
+ # create a map for faster lookup
+ BOOST_SUPPORTED_OPERATORS = dict(zip(BOOST_SUPPORTED_OPERATORS, range(len(BOOST_SUPPORTED_OPERATORS))))
+
+ # a dict of operators that are not directly supported by boost, but can be exposed
+ # simply as a function with a special name
+ BOOST_RENAME_OPERATORS = {
+ '()' : '__call__',
+ }
+
+ # converters which have a special name in python
+ # it's a map of a regular expression of the converter's result to the
+ # appropriate python name
+ SPECIAL_CONVERTERS = {
+ re.compile(r'(const)?\s*double$') : '__float__',
+ re.compile(r'(const)?\s*float$') : '__float__',
+ re.compile(r'(const)?\s*int$') : '__int__',
+ re.compile(r'(const)?\s*long$') : '__long__',
+ re.compile(r'(const)?\s*char\s*\*?$') : '__str__',
+ re.compile(r'(const)?.*::basic_string<.*>\s*(\*|\&)?$') : '__str__',
+ }
+
+
+ def ExportOperators(self):
+ 'Export all member operators and free operators related to this class'
+
+ def GetFreeOperators():
+ 'Get all the free (global) operators related to this class'
+ operators = []
+ for decl in self.declarations:
+ if isinstance(decl, Operator):
+ # check if one of the params is this class
+ for param in decl.parameters:
+ if param.name == self.class_.FullName():
+ operators.append(decl)
+ break
+ return operators
+
+ def GetOperand(param):
+ 'Returns the operand of this parameter (either "self", or "other<type>")'
+ if param.name == self.class_.FullName():
+ return namespaces.python + 'self'
+ else:
+ return namespaces.python + ('other< %s >()' % param.name)
+
+
+ def HandleSpecialOperator(operator):
+ # gatter information about the operator and its parameters
+ result_name = operator.result.name
+ param1_name = ''
+ if operator.parameters:
+ param1_name = operator.parameters[0].name
+
+ # check for str
+ ostream = 'basic_ostream'
+ is_str = result_name.find(ostream) != -1 and param1_name.find(ostream) != -1
+ if is_str:
+ namespace = namespaces.python + 'self_ns::'
+ self_ = namespaces.python + 'self'
+ return '.def(%sstr(%s))' % (namespace, self_)
+
+ # is not a special operator
+ return None
+
+
+
+ frees = GetFreeOperators()
+ members = [x for x in self.public_members if type(x) == ClassOperator]
+ all_operators = frees + members
+ operators = [x for x in all_operators if not self.info['operator'][x.name].exclude]
+
+ for operator in operators:
+ # gatter information about the operator, for use later
+ wrapper = self.info['operator'][operator.name].wrapper
+ if wrapper:
+ pointer = '&' + wrapper.FullName()
+ if wrapper.code:
+ self.Add('declaration-outside', wrapper.code)
+ else:
+ pointer = operator.PointerDeclaration()
+ rename = self.info['operator'][operator.name].rename
+
+ # check if this operator will be exported as a method
+ export_as_method = wrapper or rename or operator.name in self.BOOST_RENAME_OPERATORS
+
+ # check if this operator has a special representation in boost
+ special_code = HandleSpecialOperator(operator)
+ has_special_representation = special_code is not None
+
+ if export_as_method:
+ # export this operator as a normal method, renaming or using the given wrapper
+ if not rename:
+ if wrapper:
+ rename = wrapper.name
+ else:
+ rename = self.BOOST_RENAME_OPERATORS[operator.name]
+ policy = ''
+ policy_obj = self.info['operator'][operator.name].policy
+ if policy_obj:
+ policy = ', %s()' % policy_obj.Code()
+ self.Add('inside', '.def("%s", %s%s)' % (rename, pointer, policy))
+
+ elif has_special_representation:
+ self.Add('inside', special_code)
+
+ elif operator.name in self.BOOST_SUPPORTED_OPERATORS:
+ # export this operator using boost's facilities
+ op = operator
+ is_unary = isinstance(op, Operator) and len(op.parameters) == 1 or\
+ isinstance(op, ClassOperator) and len(op.parameters) == 0
+ if is_unary:
+ self.Add('inside', '.def( %s%sself )' % \
+ (operator.name, namespaces.python))
+ else:
+ # binary operator
+ if len(operator.parameters) == 2:
+ left_operand = GetOperand(operator.parameters[0])
+ right_operand = GetOperand(operator.parameters[1])
+ else:
+ left_operand = namespaces.python + 'self'
+ right_operand = GetOperand(operator.parameters[0])
+ self.Add('inside', '.def( %s %s %s )' % \
+ (left_operand, operator.name, right_operand))
+
+ # export the converters.
+ # export them as simple functions with a pre-determined name
+
+ converters = [x for x in self.public_members if type(x) == ConverterOperator]
+
+ def ConverterMethodName(converter):
+ result_fullname = converter.result.FullName()
+ result_name = converter.result.name
+ for regex, method_name in self.SPECIAL_CONVERTERS.items():
+ if regex.match(result_fullname):
+ return method_name
+ else:
+ # extract the last name from the full name
+ result_name = makeid(result_name)
+ return 'to_' + result_name
+
+ for converter in converters:
+ info = self.info['operator'][converter.result.FullName()]
+ # check if this operator should be excluded
+ if info.exclude:
+ continue
+
+ special_code = HandleSpecialOperator(converter)
+ if info.rename or not special_code:
+ # export as method
+ name = info.rename or ConverterMethodName(converter)
+ pointer = converter.PointerDeclaration()
+ policy_code = ''
+ if info.policy:
+ policy_code = ', %s()' % info.policy.Code()
+ self.Add('inside', '.def("%s", %s%s)' % (name, pointer, policy_code))
+
+ elif special_code:
+ self.Add('inside', special_code)
+
+
+
+ def ExportNestedClasses(self, exported_names):
+ nested_classes = [x for x in self.public_members if isinstance(x, NestedClass)]
+ for nested_class in nested_classes:
+ nested_info = self.info[nested_class.name]
+ nested_info.include = self.info.include
+ nested_info.name = nested_class.FullName()
+ exporter = self.__class__(nested_info)
+ exporter.SetDeclarations(self.declarations)
+ codeunit = SingleCodeUnit(None, None)
+ exporter.Export(codeunit, exported_names)
+ self.nested_codeunits.append(codeunit)
+
+
+ def ExportNestedEnums(self, exported_names):
+ nested_enums = [x for x in self.public_members if isinstance(x, ClassEnumeration)]
+ for enum in nested_enums:
+ enum_info = self.info[enum.name]
+ enum_info.include = self.info.include
+ enum_info.name = enum.FullName()
+ exporter = EnumExporter(enum_info)
+ exporter.SetDeclarations(self.declarations)
+ codeunit = SingleCodeUnit(None, None)
+ exporter.Export(codeunit, exported_names)
+ self.nested_codeunits.append(codeunit)
+
+
+ def ExportSmartPointer(self):
+ smart_ptr = self.info.smart_ptr
+ if smart_ptr:
+ class_name = self.class_.FullName()
+ smart_ptr = smart_ptr % class_name
+ self.Add('scope', '%sregister_ptr_to_python< %s >();' % (namespaces.python, smart_ptr))
+
+
+ def ExportOpaquePointerPolicies(self):
+ # check all methods for 'return_opaque_pointer' policies
+ methods = [x for x in self.public_members if isinstance(x, Method)]
+ for method in methods:
+ return_opaque_policy = return_value_policy(return_opaque_pointer)
+ if self.info[method.name].policy == return_opaque_policy:
+ macro = exporterutils.EspecializeTypeID(method.result.name)
+ if macro:
+ self.Add('declaration-outside', macro)
+
+ def ExportAddedCode(self):
+ if self.info.__code__:
+ for code in self.info.__code__:
+ self.Add('inside', code)
+
+
+#==============================================================================
+# Virtual Wrapper utils
+#==============================================================================
+
+def _ParamsInfo(m, count=None):
+ if count is None:
+ count = len(m.parameters)
+ param_names = ['p%i' % i for i in range(count)]
+ param_types = [x.FullName() for x in m.parameters[:count]]
+ params = ['%s %s' % (t, n) for t, n in zip(param_types, param_names)]
+ #for i, p in enumerate(m.parameters[:count]):
+ # if p.default is not None:
+ # #params[i] += '=%s' % p.default
+ # params[i] += '=%s' % (p.name + '()')
+ params = ', '.join(params)
+ return params, param_names, param_types
+
+
+class _VirtualWrapperGenerator(object):
+ 'Generates code to export the virtual methods of the given class'
+
+ def __init__(self, class_, bases, info, codeunit):
+ self.class_ = copy.deepcopy(class_)
+ self.bases = bases[:]
+ self.info = info
+ self.wrapper_name = makeid(class_.FullName()) + '_Wrapper'
+ self.virtual_methods = None
+ self._method_count = {}
+ self.codeunit = codeunit
+ self.GenerateVirtualMethods()
+
+
+ SELF = 'py_self'
+
+
+ def DefaultImplementationNames(self, method):
+ '''Returns a list of default implementations for this method, one for each
+ number of default arguments. Always returns at least one name, and return from
+ the one with most arguments to the one with the least.
+ '''
+ base_name = 'default_' + method.name
+ minArgs = method.minArgs
+ maxArgs = method.maxArgs
+ if minArgs == maxArgs:
+ return [base_name]
+ else:
+ return [base_name + ('_%i' % i) for i in range(minArgs, maxArgs+1)]
+
+
+ def Declaration(self, method, indent):
+ '''Returns a string with the declarations of the virtual wrapper and
+ its default implementations. This string must be put inside the Wrapper
+ body.
+ '''
+ pyste = namespaces.pyste
+ python = namespaces.python
+ rename = self.info[method.name].rename or method.name
+ result = method.result.FullName()
+ return_str = 'return '
+ if result == 'void':
+ return_str = ''
+ params, param_names, param_types = _ParamsInfo(method)
+ constantness = ''
+ if method.const:
+ constantness = ' const'
+
+ # call_method callback
+ decl = indent + '%s %s(%s)%s%s {\n' % (result, method.name, params, constantness, method.Exceptions())
+ param_names_str = ', '.join(param_names)
+ if param_names_str:
+ param_names_str = ', ' + param_names_str
+
+ self_str = self.SELF
+
+ decl += indent*2 + '%(return_str)s%(python)scall_method< %(result)s >' \
+ '(%(self_str)s, "%(rename)s"%(param_names_str)s);\n' % locals()
+ decl += indent + '}\n'
+
+ # default implementations (with overloading)
+ def DefaultImpl(method, param_names):
+ 'Return the body of a default implementation wrapper'
+ indent2 = indent * 2
+ wrapper = self.info[method.name].wrapper
+ if not wrapper:
+ # return the default implementation of the class
+ return indent2 + '%s%s(%s);\n' % \
+ (return_str, method.FullName(), ', '.join(param_names))
+ else:
+ if wrapper.code:
+ self.codeunit.Write('declaration-outside', wrapper.code)
+ # return a call for the wrapper
+ params = ', '.join(['this'] + param_names)
+ return indent2 + '%s%s(%s);\n' % (return_str, wrapper.FullName(), params)
+
+ if not method.abstract and method.visibility != Scope.private:
+ minArgs = method.minArgs
+ maxArgs = method.maxArgs
+ impl_names = self.DefaultImplementationNames(method)
+ for impl_name, argNum in zip(impl_names, range(minArgs, maxArgs+1)):
+ params, param_names, param_types = _ParamsInfo(method, argNum)
+ decl += '\n'
+ decl += indent + '%s %s(%s)%s {\n' % (result, impl_name, params, constantness)
+ decl += DefaultImpl(method, param_names)
+ decl += indent + '}\n'
+ return decl
+
+
+ def MethodDefinition(self, method):
+ '''Returns a list of lines, which should be put inside the class_
+ statement to export this method.'''
+ # dont define abstract methods
+ pyste = namespaces.pyste
+ rename = self.info[method.name].rename or method.name
+ default_names = self.DefaultImplementationNames(method)
+ class_name = self.class_.FullName()
+ wrapper_name = pyste + self.wrapper_name
+ result = method.result.FullName()
+ is_method_unique = method.is_unique
+ constantness = ''
+ if method.const:
+ constantness = ' const'
+
+ # create a list of default-impl pointers
+ minArgs = method.minArgs
+ maxArgs = method.maxArgs
+ if method.abstract:
+ default_pointers = []
+ elif is_method_unique:
+ default_pointers = ['&%s::%s' % (wrapper_name, x) for x in default_names]
+ else:
+ default_pointers = []
+ for impl_name, argNum in zip(default_names, range(minArgs, maxArgs+1)):
+ param_list = [x.FullName() for x in method.parameters[:argNum]]
+ params = ', '.join(param_list)
+ signature = '%s (%s::*)(%s)%s' % (result, wrapper_name, params, constantness)
+ default_pointer = '(%s)&%s::%s' % (signature, wrapper_name, impl_name)
+ default_pointers.append(default_pointer)
+
+ # get the pointer of the method
+ pointer = method.PointerDeclaration()
+ if method.abstract:
+ pointer = namespaces.python + ('pure_virtual(%s)' % pointer)
+
+ # warn the user if this method needs a policy and doesn't have one
+ method_info = self.info[method.name]
+ method_info.policy = exporterutils.HandlePolicy(method, method_info.policy)
+
+ # Add policy to overloaded methods also
+ policy = method_info.policy or ''
+ if policy:
+ policy = ', %s%s()' % (namespaces.python, policy.Code())
+
+ # generate the defs
+ definitions = []
+ # basic def
+ if default_pointers:
+ definitions.append('.def("%s", %s, %s%s)' % (rename, pointer, default_pointers[-1], policy))
+ for default_pointer in default_pointers[:-1]:
+ definitions.append('.def("%s", %s%s)' % (rename, default_pointer, policy))
+ else:
+ definitions.append('.def("%s", %s%s)' % (rename, pointer, policy))
+ return definitions
+
+
+ def FullName(self):
+ return namespaces.pyste + self.wrapper_name
+
+
+ def GenerateVirtualMethods(self):
+ '''To correctly export all virtual methods, we must also make wrappers
+ for the virtual methods of the bases of this class, as if the methods
+ were from this class itself.
+ This method creates the instance variable self.virtual_methods.
+ '''
+ def IsVirtual(m):
+ if type(m) is Method:
+ pure_virtual = m.abstract and m.virtual
+ virtual = m.virtual and m.visibility != Scope.private
+ return virtual or pure_virtual
+ else:
+ return False
+
+ # extract the virtual methods, avoiding duplications. The duplication
+ # must take in account the full signature without the class name, so
+ # that inherited members are correctly excluded if the subclass overrides
+ # them.
+ def MethodSig(method):
+ if method.const:
+ const = ' const'
+ else:
+ const = ''
+ if method.result:
+ result = method.result.FullName()
+ else:
+ result = ''
+ params = ', '.join([x.FullName() for x in method.parameters])
+ return '%s %s(%s)%s%s' % (
+ result, method.name, params, const, method.Exceptions())
+
+ already_added = {}
+ self.virtual_methods = []
+ for member in self.class_:
+ if IsVirtual(member):
+ already_added[MethodSig(member)] = None
+ self.virtual_methods.append(member)
+
+ for base in self.bases:
+ base_methods = [copy.deepcopy(x) for x in base if IsVirtual(x)]
+ for base_method in base_methods:
+ self.class_.AddMember(base_method)
+
+ all_methods = [x for x in self.class_ if IsVirtual(x)]
+
+ for member in all_methods:
+ sig = MethodSig(member)
+ if IsVirtual(member) and not sig in already_added:
+ self.virtual_methods.append(member)
+ already_added[sig] = 0
+
+
+ def Constructors(self):
+ return self.class_.Constructors(publics_only=True)
+
+
+ def GenerateDefinitions(self):
+ defs = []
+ for method in self.virtual_methods:
+ exclude = self.info[method.name].exclude
+ # generate definitions only for public methods and non-abstract methods
+ if method.visibility == Scope.public and not exclude:
+ defs.extend(self.MethodDefinition(method))
+ return defs
+
+
+ def GenerateVirtualWrapper(self, indent):
+ 'Return the wrapper for this class'
+
+ # generate the class code
+ class_name = self.class_.FullName()
+ code = 'struct %s: %s\n' % (self.wrapper_name, class_name)
+ code += '{\n'
+ # generate constructors (with the overloads for each one)
+ for cons in self.Constructors(): # only public constructors
+ minArgs = cons.minArgs
+ maxArgs = cons.maxArgs
+ # from the min number of arguments to the max number, generate
+ # all version of the given constructor
+ cons_code = ''
+ for argNum in range(minArgs, maxArgs+1):
+ params, param_names, param_types = _ParamsInfo(cons, argNum)
+ if params:
+ params = ', ' + params
+ cons_code += indent + '%s(PyObject* %s_%s):\n' % \
+ (self.wrapper_name, self.SELF, params)
+ cons_code += indent*2 + '%s(%s), %s(%s_) {}\n\n' % \
+ (class_name, ', '.join(param_names), self.SELF, self.SELF)
+ code += cons_code
+ # generate the body
+ body = []
+ for method in self.virtual_methods:
+ if not self.info[method.name].exclude:
+ body.append(self.Declaration(method, indent))
+ body = '\n'.join(body)
+ code += body + '\n'
+ # add the self member
+ code += indent + 'PyObject* %s;\n' % self.SELF
+ code += '};\n'
+ return code