| # 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 |