1# Copyright Bruno da Silva de Oliveira 2003. Use, modification and 2# distribution is subject to the Boost Software License, Version 1.0. 3# (See accompanying file LICENSE_1_0.txt or copy at 4# http://www.boost.org/LICENSE_1_0.txt) 5 6import exporters 7from Exporter import Exporter 8from declarations import * 9from settings import * 10from policies import * 11from SingleCodeUnit import SingleCodeUnit 12from EnumExporter import EnumExporter 13from utils import makeid, enumerate 14import copy 15import exporterutils 16import re 17 18#============================================================================== 19# ClassExporter 20#============================================================================== 21class ClassExporter(Exporter): 22 'Generates boost.python code to export a class declaration' 23 24 def __init__(self, info, parser_tail=None): 25 Exporter.__init__(self, info, parser_tail) 26 # sections of code 27 self.sections = {} 28 # template: each item in the list is an item into the class_<...> 29 # section. 30 self.sections['template'] = [] 31 # constructor: each item in the list is a parameter to the class_ 32 # constructor, like class_<C>(...) 33 self.sections['constructor'] = [] 34 # inside: everything within the class_<> statement 35 self.sections['inside'] = [] 36 # scope: items outside the class statement but within its scope. 37 # scope* s = new scope(class<>()); 38 # ... 39 # delete s; 40 self.sections['scope'] = [] 41 # declarations: outside the BOOST_PYTHON_MODULE macro 42 self.sections['declaration'] = [] 43 self.sections['declaration-outside'] = [] 44 self.sections['include'] = [] 45 # a list of Constructor instances 46 self.constructors = [] 47 # a list of code units, generated by nested declarations 48 self.nested_codeunits = [] 49 50 51 def ScopeName(self): 52 return makeid(self.class_.FullName()) + '_scope' 53 54 55 def Name(self): 56 return self.info.name 57 58 59 def SetDeclarations(self, declarations): 60 Exporter.SetDeclarations(self, declarations) 61 if self.declarations: 62 decl = self.GetDeclaration(self.info.name) 63 if isinstance(decl, Typedef): 64 self.class_ = self.GetDeclaration(decl.type.name) 65 if not self.info.rename: 66 self.info.rename = decl.name 67 else: 68 self.class_ = decl 69 self.class_ = copy.deepcopy(self.class_) 70 else: 71 self.class_ = None 72 73 74 def ClassBases(self): 75 all_bases = [] 76 for level in self.class_.hierarchy: 77 for base in level: 78 all_bases.append(base) 79 return [self.GetDeclaration(x.name) for x in all_bases] 80 81 82 def Order(self): 83 '''Return the TOTAL number of bases that this class has, including the 84 bases' bases. Do this because base classes must be instantialized 85 before the derived classes in the module definition. 86 ''' 87 num_bases = len(self.ClassBases()) 88 return num_bases, self.class_.FullName() 89 90 91 def Export(self, codeunit, exported_names): 92 self.InheritMethods(exported_names) 93 self.MakeNonVirtual() 94 if not self.info.exclude: 95 self.ExportBasics() 96 self.ExportBases(exported_names) 97 self.ExportConstructors() 98 self.ExportVariables() 99 self.ExportVirtualMethods(codeunit) 100 self.ExportMethods() 101 self.ExportOperators() 102 self.ExportNestedClasses(exported_names) 103 self.ExportNestedEnums(exported_names) 104 self.ExportSmartPointer() 105 self.ExportOpaquePointerPolicies() 106 self.ExportAddedCode() 107 self.Write(codeunit) 108 exported_names[self.Name()] = 1 109 110 111 def InheritMethods(self, exported_names): 112 '''Go up in the class hierarchy looking for classes that were not 113 exported yet, and then add their public members to this classes 114 members, as if they were members of this class. This allows the user to 115 just export one type and automatically get all the members from the 116 base classes. 117 ''' 118 valid_members = (Method, ClassVariable, NestedClass, ClassEnumeration) 119 fullnames = [x.FullName() for x in self.class_] 120 pointers = [x.PointerDeclaration(True) for x in self.class_ if isinstance(x, Method)] 121 fullnames = dict([(x, None) for x in fullnames]) 122 pointers = dict([(x, None) for x in pointers]) 123 for level in self.class_.hierarchy: 124 level_exported = False 125 for base in level: 126 base = self.GetDeclaration(base.name) 127 if base.FullName() not in exported_names: 128 for member in base: 129 if type(member) in valid_members: 130 member_copy = copy.deepcopy(member) 131 member_copy.class_ = self.class_.FullName() 132 if isinstance(member_copy, Method): 133 pointer = member_copy.PointerDeclaration(True) 134 if pointer not in pointers: 135 self.class_.AddMember(member) 136 pointers[pointer] = None 137 elif member_copy.FullName() not in fullnames: 138 self.class_.AddMember(member) 139 else: 140 level_exported = True 141 if level_exported: 142 break 143 def IsValid(member): 144 return isinstance(member, valid_members) and member.visibility == Scope.public 145 self.public_members = [x for x in self.class_ if IsValid(x)] 146 147 148 def Write(self, codeunit): 149 indent = self.INDENT 150 boost_ns = namespaces.python 151 pyste_ns = namespaces.pyste 152 code = '' 153 # begin a scope for this class if needed 154 nested_codeunits = self.nested_codeunits 155 needs_scope = self.sections['scope'] or nested_codeunits 156 if needs_scope: 157 scope_name = self.ScopeName() 158 code += indent + boost_ns + 'scope* %s = new %sscope(\n' %\ 159 (scope_name, boost_ns) 160 # export the template section 161 template_params = ', '.join(self.sections['template']) 162 code += indent + boost_ns + 'class_< %s >' % template_params 163 # export the constructor section 164 constructor_params = ', '.join(self.sections['constructor']) 165 code += '(%s)\n' % constructor_params 166 # export the inside section 167 in_indent = indent*2 168 for line in self.sections['inside']: 169 code += in_indent + line + '\n' 170 # write the scope section and end it 171 if not needs_scope: 172 code += indent + ';\n' 173 else: 174 code += indent + ');\n' 175 for line in self.sections['scope']: 176 code += indent + line + '\n' 177 # write the contents of the nested classes 178 for nested_unit in nested_codeunits: 179 code += '\n' + nested_unit.Section('module') 180 # close the scope 181 code += indent + 'delete %s;\n' % scope_name 182 183 # write the code to the module section in the codeunit 184 codeunit.Write('module', code + '\n') 185 186 # write the declarations to the codeunit 187 declarations = '\n'.join(self.sections['declaration']) 188 for nested_unit in nested_codeunits: 189 declarations += nested_unit.Section('declaration') 190 if declarations: 191 codeunit.Write('declaration', declarations + '\n') 192 declarations_outside = '\n'.join(self.sections['declaration-outside']) 193 if declarations_outside: 194 codeunit.Write('declaration-outside', declarations_outside + '\n') 195 196 # write the includes to the codeunit 197 includes = '\n'.join(self.sections['include']) 198 for nested_unit in nested_codeunits: 199 includes += nested_unit.Section('include') 200 if includes: 201 codeunit.Write('include', includes) 202 203 204 def Add(self, section, item): 205 'Add the item into the corresponding section' 206 self.sections[section].append(item) 207 208 209 def ExportBasics(self): 210 '''Export the name of the class and its class_ statement.''' 211 class_name = self.class_.FullName() 212 self.Add('template', class_name) 213 name = self.info.rename or self.class_.name 214 self.Add('constructor', '"%s"' % name) 215 216 217 def ExportBases(self, exported_names): 218 'Expose the bases of the class into the template section' 219 hierarchy = self.class_.hierarchy 220 exported = [] 221 for level in hierarchy: 222 for base in level: 223 if base.visibility == Scope.public and base.name in exported_names: 224 exported.append(base.name) 225 if exported: 226 break 227 if exported: 228 code = namespaces.python + 'bases< %s > ' % (', '.join(exported)) 229 self.Add('template', code) 230 231 232 def ExportConstructors(self): 233 '''Exports all the public contructors of the class, plus indicates if the 234 class is noncopyable. 235 ''' 236 py_ns = namespaces.python 237 indent = self.INDENT 238 239 def init_code(cons): 240 'return the init<>() code for the given contructor' 241 param_list = [p.FullName() for p in cons.parameters] 242 min_params_list = param_list[:cons.minArgs] 243 max_params_list = param_list[cons.minArgs:] 244 min_params = ', '.join(min_params_list) 245 max_params = ', '.join(max_params_list) 246 init = py_ns + 'init< ' 247 init += min_params 248 if max_params: 249 if min_params: 250 init += ', ' 251 init += py_ns + ('optional< %s >' % max_params) 252 init += ' >()' 253 return init 254 255 constructors = [x for x in self.public_members if isinstance(x, Constructor)] 256 # don't export copy constructors if the class is abstract 257 # we could remove all constructors, but this will have the effect of 258 # inserting no_init in the declaration, which would not allow 259 # even subclasses to be instantiated. 260 self.constructors = constructors[:] 261 if self.class_.abstract: 262 for cons in constructors: 263 if cons.IsCopy(): 264 constructors.remove(cons) 265 break 266 267 if not constructors: 268 # declare no_init 269 self.Add('constructor', py_ns + 'no_init') 270 else: 271 # write the constructor with less parameters to the constructor section 272 smaller = None 273 for cons in constructors: 274 if smaller is None or len(cons.parameters) < len(smaller.parameters): 275 smaller = cons 276 assert smaller is not None 277 self.Add('constructor', init_code(smaller)) 278 constructors.remove(smaller) 279 # write the rest to the inside section, using def() 280 for cons in constructors: 281 code = '.def(%s)' % init_code(cons) 282 self.Add('inside', code) 283 284 # check if the class is copyable 285 if not self.class_.HasCopyConstructor() or self.class_.abstract: 286 self.Add('template', namespaces.boost + 'noncopyable') 287 288 289 def ExportVariables(self): 290 'Export the variables of the class, both static and simple variables' 291 vars = [x for x in self.public_members if isinstance(x, Variable)] 292 for var in vars: 293 if self.info[var.name].exclude: 294 continue 295 name = self.info[var.name].rename or var.name 296 fullname = var.FullName() 297 if var.type.const: 298 def_ = '.def_readonly' 299 else: 300 def_ = '.def_readwrite' 301 code = '%s("%s", &%s)' % (def_, name, fullname) 302 self.Add('inside', code) 303 304 305 def OverloadName(self, method): 306 'Returns the name of the overloads struct for the given method' 307 name = makeid(method.FullName()) 308 overloads = '_overloads_%i_%i' % (method.minArgs, method.maxArgs) 309 return name + overloads 310 311 312 def GetAddedMethods(self): 313 added_methods = self.info.__added__ 314 result = [] 315 if added_methods: 316 for name, rename in added_methods: 317 decl = self.GetDeclaration(name) 318 self.info[name].rename = rename 319 result.append(decl) 320 return result 321 322 323 def ExportMethods(self): 324 '''Export all the non-virtual methods of this class, plus any function 325 that is to be exported as a method''' 326 327 declared = {} 328 def DeclareOverloads(m): 329 'Declares the macro for the generation of the overloads' 330 if (isinstance(m, Method) and m.static) or type(m) == Function: 331 func = m.FullName() 332 macro = 'BOOST_PYTHON_FUNCTION_OVERLOADS' 333 else: 334 func = m.name 335 macro = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS' 336 code = '%s(%s, %s, %i, %i)\n' % (macro, self.OverloadName(m), func, m.minArgs, m.maxArgs) 337 if code not in declared: 338 declared[code] = True 339 self.Add('declaration', code) 340 341 342 def Pointer(m): 343 'returns the correct pointer declaration for the method m' 344 # check if this method has a wrapper set for him 345 wrapper = self.info[m.name].wrapper 346 if wrapper: 347 return '&' + wrapper.FullName() 348 else: 349 return m.PointerDeclaration() 350 351 def IsExportable(m): 352 'Returns true if the given method is exportable by this routine' 353 ignore = (Constructor, ClassOperator, Destructor) 354 return isinstance(m, Function) and not isinstance(m, ignore) and not m.virtual 355 356 methods = [x for x in self.public_members if IsExportable(x)] 357 methods.extend(self.GetAddedMethods()) 358 359 staticmethods = {} 360 361 for method in methods: 362 method_info = self.info[method.name] 363 364 # skip this method if it was excluded by the user 365 if method_info.exclude: 366 continue 367 368 # rename the method if the user requested 369 name = method_info.rename or method.name 370 371 # warn the user if this method needs a policy and doesn't have one 372 method_info.policy = exporterutils.HandlePolicy(method, method_info.policy) 373 374 # check for policies 375 policy = method_info.policy or '' 376 if policy: 377 policy = ', %s%s()' % (namespaces.python, policy.Code()) 378 # check for overloads 379 overload = '' 380 if method.minArgs != method.maxArgs and not method_info.wrapper: 381 # add the overloads for this method 382 DeclareOverloads(method) 383 overload_name = self.OverloadName(method) 384 overload = ', %s%s()' % (namespaces.pyste, overload_name) 385 386 # build the .def string to export the method 387 pointer = Pointer(method) 388 code = '.def("%s", %s' % (name, pointer) 389 code += policy 390 code += overload 391 code += ')' 392 self.Add('inside', code) 393 # static method 394 if isinstance(method, Method) and method.static: 395 staticmethods[name] = 1 396 # add wrapper code if this method has one 397 wrapper = method_info.wrapper 398 if wrapper and wrapper.code: 399 self.Add('declaration', wrapper.code) 400 401 # export staticmethod statements 402 for name in staticmethods: 403 code = '.staticmethod("%s")' % name 404 self.Add('inside', code) 405 406 407 408 def MakeNonVirtual(self): 409 '''Make all methods that the user indicated to no_override no more virtual, delegating their 410 export to the ExportMethods routine''' 411 for member in self.class_: 412 if type(member) == Method and member.virtual: 413 member.virtual = not self.info[member.name].no_override 414 415 416 def ExportVirtualMethods(self, codeunit): 417 # check if this class has any virtual methods 418 has_virtual_methods = False 419 for member in self.class_: 420 if type(member) == Method and member.virtual: 421 has_virtual_methods = True 422 break 423 424 holder = self.info.holder 425 if has_virtual_methods: 426 generator = _VirtualWrapperGenerator(self.class_, self.ClassBases(), self.info, codeunit) 427 if holder: 428 self.Add('template', holder(generator.FullName())) 429 else: 430 self.Add('template', generator.FullName()) 431 for definition in generator.GenerateDefinitions(): 432 self.Add('inside', definition) 433 self.Add('declaration', generator.GenerateVirtualWrapper(self.INDENT)) 434 else: 435 if holder: 436 self.Add('template', holder(self.class_.FullName())) 437 438 # operators natively supported by boost 439 BOOST_SUPPORTED_OPERATORS = '+ - * / % ^ & ! ~ | < > == != <= >= << >> && || += -= '\ 440 '*= /= %= ^= &= |= <<= >>='.split() 441 # create a map for faster lookup 442 BOOST_SUPPORTED_OPERATORS = dict(zip(BOOST_SUPPORTED_OPERATORS, range(len(BOOST_SUPPORTED_OPERATORS)))) 443 444 # a dict of operators that are not directly supported by boost, but can be exposed 445 # simply as a function with a special name 446 BOOST_RENAME_OPERATORS = { 447 '()' : '__call__', 448 } 449 450 # converters which have a special name in python 451 # it's a map of a regular expression of the converter's result to the 452 # appropriate python name 453 SPECIAL_CONVERTERS = { 454 re.compile(r'(const)?\s*double$') : '__float__', 455 re.compile(r'(const)?\s*float$') : '__float__', 456 re.compile(r'(const)?\s*int$') : '__int__', 457 re.compile(r'(const)?\s*long$') : '__long__', 458 re.compile(r'(const)?\s*char\s*\*?$') : '__str__', 459 re.compile(r'(const)?.*::basic_string<.*>\s*(\*|\&)?$') : '__str__', 460 } 461 462 463 def ExportOperators(self): 464 'Export all member operators and free operators related to this class' 465 466 def GetFreeOperators(): 467 'Get all the free (global) operators related to this class' 468 operators = [] 469 for decl in self.declarations: 470 if isinstance(decl, Operator): 471 # check if one of the params is this class 472 for param in decl.parameters: 473 if param.name == self.class_.FullName(): 474 operators.append(decl) 475 break 476 return operators 477 478 def GetOperand(param): 479 'Returns the operand of this parameter (either "self", or "other<type>")' 480 if param.name == self.class_.FullName(): 481 return namespaces.python + 'self' 482 else: 483 return namespaces.python + ('other< %s >()' % param.name) 484 485 486 def HandleSpecialOperator(operator): 487 # gatter information about the operator and its parameters 488 result_name = operator.result.name 489 param1_name = '' 490 if operator.parameters: 491 param1_name = operator.parameters[0].name 492 493 # check for str 494 ostream = 'basic_ostream' 495 is_str = result_name.find(ostream) != -1 and param1_name.find(ostream) != -1 496 if is_str: 497 namespace = namespaces.python + 'self_ns::' 498 self_ = namespaces.python + 'self' 499 return '.def(%sstr(%s))' % (namespace, self_) 500 501 # is not a special operator 502 return None 503 504 505 506 frees = GetFreeOperators() 507 members = [x for x in self.public_members if type(x) == ClassOperator] 508 all_operators = frees + members 509 operators = [x for x in all_operators if not self.info['operator'][x.name].exclude] 510 511 for operator in operators: 512 # gatter information about the operator, for use later 513 wrapper = self.info['operator'][operator.name].wrapper 514 if wrapper: 515 pointer = '&' + wrapper.FullName() 516 if wrapper.code: 517 self.Add('declaration-outside', wrapper.code) 518 else: 519 pointer = operator.PointerDeclaration() 520 rename = self.info['operator'][operator.name].rename 521 522 # check if this operator will be exported as a method 523 export_as_method = wrapper or rename or operator.name in self.BOOST_RENAME_OPERATORS 524 525 # check if this operator has a special representation in boost 526 special_code = HandleSpecialOperator(operator) 527 has_special_representation = special_code is not None 528 529 if export_as_method: 530 # export this operator as a normal method, renaming or using the given wrapper 531 if not rename: 532 if wrapper: 533 rename = wrapper.name 534 else: 535 rename = self.BOOST_RENAME_OPERATORS[operator.name] 536 policy = '' 537 policy_obj = self.info['operator'][operator.name].policy 538 if policy_obj: 539 policy = ', %s()' % policy_obj.Code() 540 self.Add('inside', '.def("%s", %s%s)' % (rename, pointer, policy)) 541 542 elif has_special_representation: 543 self.Add('inside', special_code) 544 545 elif operator.name in self.BOOST_SUPPORTED_OPERATORS: 546 # export this operator using boost's facilities 547 op = operator 548 is_unary = isinstance(op, Operator) and len(op.parameters) == 1 or\ 549 isinstance(op, ClassOperator) and len(op.parameters) == 0 550 if is_unary: 551 self.Add('inside', '.def( %s%sself )' % \ 552 (operator.name, namespaces.python)) 553 else: 554 # binary operator 555 if len(operator.parameters) == 2: 556 left_operand = GetOperand(operator.parameters[0]) 557 right_operand = GetOperand(operator.parameters[1]) 558 else: 559 left_operand = namespaces.python + 'self' 560 right_operand = GetOperand(operator.parameters[0]) 561 self.Add('inside', '.def( %s %s %s )' % \ 562 (left_operand, operator.name, right_operand)) 563 564 # export the converters. 565 # export them as simple functions with a pre-determined name 566 567 converters = [x for x in self.public_members if type(x) == ConverterOperator] 568 569 def ConverterMethodName(converter): 570 result_fullname = converter.result.FullName() 571 result_name = converter.result.name 572 for regex, method_name in self.SPECIAL_CONVERTERS.items(): 573 if regex.match(result_fullname): 574 return method_name 575 else: 576 # extract the last name from the full name 577 result_name = makeid(result_name) 578 return 'to_' + result_name 579 580 for converter in converters: 581 info = self.info['operator'][converter.result.FullName()] 582 # check if this operator should be excluded 583 if info.exclude: 584 continue 585 586 special_code = HandleSpecialOperator(converter) 587 if info.rename or not special_code: 588 # export as method 589 name = info.rename or ConverterMethodName(converter) 590 pointer = converter.PointerDeclaration() 591 policy_code = '' 592 if info.policy: 593 policy_code = ', %s()' % info.policy.Code() 594 self.Add('inside', '.def("%s", %s%s)' % (name, pointer, policy_code)) 595 596 elif special_code: 597 self.Add('inside', special_code) 598 599 600 601 def ExportNestedClasses(self, exported_names): 602 nested_classes = [x for x in self.public_members if isinstance(x, NestedClass)] 603 for nested_class in nested_classes: 604 nested_info = self.info[nested_class.name] 605 nested_info.include = self.info.include 606 nested_info.name = nested_class.FullName() 607 exporter = self.__class__(nested_info) 608 exporter.SetDeclarations(self.declarations) 609 codeunit = SingleCodeUnit(None, None) 610 exporter.Export(codeunit, exported_names) 611 self.nested_codeunits.append(codeunit) 612 613 614 def ExportNestedEnums(self, exported_names): 615 nested_enums = [x for x in self.public_members if isinstance(x, ClassEnumeration)] 616 for enum in nested_enums: 617 enum_info = self.info[enum.name] 618 enum_info.include = self.info.include 619 enum_info.name = enum.FullName() 620 exporter = EnumExporter(enum_info) 621 exporter.SetDeclarations(self.declarations) 622 codeunit = SingleCodeUnit(None, None) 623 exporter.Export(codeunit, exported_names) 624 self.nested_codeunits.append(codeunit) 625 626 627 def ExportSmartPointer(self): 628 smart_ptr = self.info.smart_ptr 629 if smart_ptr: 630 class_name = self.class_.FullName() 631 smart_ptr = smart_ptr % class_name 632 self.Add('scope', '%sregister_ptr_to_python< %s >();' % (namespaces.python, smart_ptr)) 633 634 635 def ExportOpaquePointerPolicies(self): 636 # check all methods for 'return_opaque_pointer' policies 637 methods = [x for x in self.public_members if isinstance(x, Method)] 638 for method in methods: 639 return_opaque_policy = return_value_policy(return_opaque_pointer) 640 if self.info[method.name].policy == return_opaque_policy: 641 macro = exporterutils.EspecializeTypeID(method.result.name) 642 if macro: 643 self.Add('declaration-outside', macro) 644 645 def ExportAddedCode(self): 646 if self.info.__code__: 647 for code in self.info.__code__: 648 self.Add('inside', code) 649 650 651#============================================================================== 652# Virtual Wrapper utils 653#============================================================================== 654 655def _ParamsInfo(m, count=None): 656 if count is None: 657 count = len(m.parameters) 658 param_names = ['p%i' % i for i in range(count)] 659 param_types = [x.FullName() for x in m.parameters[:count]] 660 params = ['%s %s' % (t, n) for t, n in zip(param_types, param_names)] 661 #for i, p in enumerate(m.parameters[:count]): 662 # if p.default is not None: 663 # #params[i] += '=%s' % p.default 664 # params[i] += '=%s' % (p.name + '()') 665 params = ', '.join(params) 666 return params, param_names, param_types 667 668 669class _VirtualWrapperGenerator(object): 670 'Generates code to export the virtual methods of the given class' 671 672 def __init__(self, class_, bases, info, codeunit): 673 self.class_ = copy.deepcopy(class_) 674 self.bases = bases[:] 675 self.info = info 676 self.wrapper_name = makeid(class_.FullName()) + '_Wrapper' 677 self.virtual_methods = None 678 self._method_count = {} 679 self.codeunit = codeunit 680 self.GenerateVirtualMethods() 681 682 683 SELF = 'py_self' 684 685 686 def DefaultImplementationNames(self, method): 687 '''Returns a list of default implementations for this method, one for each 688 number of default arguments. Always returns at least one name, and return from 689 the one with most arguments to the one with the least. 690 ''' 691 base_name = 'default_' + method.name 692 minArgs = method.minArgs 693 maxArgs = method.maxArgs 694 if minArgs == maxArgs: 695 return [base_name] 696 else: 697 return [base_name + ('_%i' % i) for i in range(minArgs, maxArgs+1)] 698 699 700 def Declaration(self, method, indent): 701 '''Returns a string with the declarations of the virtual wrapper and 702 its default implementations. This string must be put inside the Wrapper 703 body. 704 ''' 705 pyste = namespaces.pyste 706 python = namespaces.python 707 rename = self.info[method.name].rename or method.name 708 result = method.result.FullName() 709 return_str = 'return ' 710 if result == 'void': 711 return_str = '' 712 params, param_names, param_types = _ParamsInfo(method) 713 constantness = '' 714 if method.const: 715 constantness = ' const' 716 717 # call_method callback 718 decl = indent + '%s %s(%s)%s%s {\n' % (result, method.name, params, constantness, method.Exceptions()) 719 param_names_str = ', '.join(param_names) 720 if param_names_str: 721 param_names_str = ', ' + param_names_str 722 723 self_str = self.SELF 724 725 decl += indent*2 + '%(return_str)s%(python)scall_method< %(result)s >' \ 726 '(%(self_str)s, "%(rename)s"%(param_names_str)s);\n' % locals() 727 decl += indent + '}\n' 728 729 # default implementations (with overloading) 730 def DefaultImpl(method, param_names): 731 'Return the body of a default implementation wrapper' 732 indent2 = indent * 2 733 wrapper = self.info[method.name].wrapper 734 if not wrapper: 735 # return the default implementation of the class 736 return indent2 + '%s%s(%s);\n' % \ 737 (return_str, method.FullName(), ', '.join(param_names)) 738 else: 739 if wrapper.code: 740 self.codeunit.Write('declaration-outside', wrapper.code) 741 # return a call for the wrapper 742 params = ', '.join(['this'] + param_names) 743 return indent2 + '%s%s(%s);\n' % (return_str, wrapper.FullName(), params) 744 745 if not method.abstract and method.visibility != Scope.private: 746 minArgs = method.minArgs 747 maxArgs = method.maxArgs 748 impl_names = self.DefaultImplementationNames(method) 749 for impl_name, argNum in zip(impl_names, range(minArgs, maxArgs+1)): 750 params, param_names, param_types = _ParamsInfo(method, argNum) 751 decl += '\n' 752 decl += indent + '%s %s(%s)%s {\n' % (result, impl_name, params, constantness) 753 decl += DefaultImpl(method, param_names) 754 decl += indent + '}\n' 755 return decl 756 757 758 def MethodDefinition(self, method): 759 '''Returns a list of lines, which should be put inside the class_ 760 statement to export this method.''' 761 # dont define abstract methods 762 pyste = namespaces.pyste 763 rename = self.info[method.name].rename or method.name 764 default_names = self.DefaultImplementationNames(method) 765 class_name = self.class_.FullName() 766 wrapper_name = pyste + self.wrapper_name 767 result = method.result.FullName() 768 is_method_unique = method.is_unique 769 constantness = '' 770 if method.const: 771 constantness = ' const' 772 773 # create a list of default-impl pointers 774 minArgs = method.minArgs 775 maxArgs = method.maxArgs 776 if method.abstract: 777 default_pointers = [] 778 elif is_method_unique: 779 default_pointers = ['&%s::%s' % (wrapper_name, x) for x in default_names] 780 else: 781 default_pointers = [] 782 for impl_name, argNum in zip(default_names, range(minArgs, maxArgs+1)): 783 param_list = [x.FullName() for x in method.parameters[:argNum]] 784 params = ', '.join(param_list) 785 signature = '%s (%s::*)(%s)%s' % (result, wrapper_name, params, constantness) 786 default_pointer = '(%s)&%s::%s' % (signature, wrapper_name, impl_name) 787 default_pointers.append(default_pointer) 788 789 # get the pointer of the method 790 pointer = method.PointerDeclaration() 791 if method.abstract: 792 pointer = namespaces.python + ('pure_virtual(%s)' % pointer) 793 794 # warn the user if this method needs a policy and doesn't have one 795 method_info = self.info[method.name] 796 method_info.policy = exporterutils.HandlePolicy(method, method_info.policy) 797 798 # Add policy to overloaded methods also 799 policy = method_info.policy or '' 800 if policy: 801 policy = ', %s%s()' % (namespaces.python, policy.Code()) 802 803 # generate the defs 804 definitions = [] 805 # basic def 806 if default_pointers: 807 definitions.append('.def("%s", %s, %s%s)' % (rename, pointer, default_pointers[-1], policy)) 808 for default_pointer in default_pointers[:-1]: 809 definitions.append('.def("%s", %s%s)' % (rename, default_pointer, policy)) 810 else: 811 definitions.append('.def("%s", %s%s)' % (rename, pointer, policy)) 812 return definitions 813 814 815 def FullName(self): 816 return namespaces.pyste + self.wrapper_name 817 818 819 def GenerateVirtualMethods(self): 820 '''To correctly export all virtual methods, we must also make wrappers 821 for the virtual methods of the bases of this class, as if the methods 822 were from this class itself. 823 This method creates the instance variable self.virtual_methods. 824 ''' 825 def IsVirtual(m): 826 if type(m) is Method: 827 pure_virtual = m.abstract and m.virtual 828 virtual = m.virtual and m.visibility != Scope.private 829 return virtual or pure_virtual 830 else: 831 return False 832 833 # extract the virtual methods, avoiding duplications. The duplication 834 # must take in account the full signature without the class name, so 835 # that inherited members are correctly excluded if the subclass overrides 836 # them. 837 def MethodSig(method): 838 if method.const: 839 const = ' const' 840 else: 841 const = '' 842 if method.result: 843 result = method.result.FullName() 844 else: 845 result = '' 846 params = ', '.join([x.FullName() for x in method.parameters]) 847 return '%s %s(%s)%s%s' % ( 848 result, method.name, params, const, method.Exceptions()) 849 850 already_added = {} 851 self.virtual_methods = [] 852 for member in self.class_: 853 if IsVirtual(member): 854 already_added[MethodSig(member)] = None 855 self.virtual_methods.append(member) 856 857 for base in self.bases: 858 base_methods = [copy.deepcopy(x) for x in base if IsVirtual(x)] 859 for base_method in base_methods: 860 self.class_.AddMember(base_method) 861 862 all_methods = [x for x in self.class_ if IsVirtual(x)] 863 864 for member in all_methods: 865 sig = MethodSig(member) 866 if IsVirtual(member) and not sig in already_added: 867 self.virtual_methods.append(member) 868 already_added[sig] = 0 869 870 871 def Constructors(self): 872 return self.class_.Constructors(publics_only=True) 873 874 875 def GenerateDefinitions(self): 876 defs = [] 877 for method in self.virtual_methods: 878 exclude = self.info[method.name].exclude 879 # generate definitions only for public methods and non-abstract methods 880 if method.visibility == Scope.public and not exclude: 881 defs.extend(self.MethodDefinition(method)) 882 return defs 883 884 885 def GenerateVirtualWrapper(self, indent): 886 'Return the wrapper for this class' 887 888 # generate the class code 889 class_name = self.class_.FullName() 890 code = 'struct %s: %s\n' % (self.wrapper_name, class_name) 891 code += '{\n' 892 # generate constructors (with the overloads for each one) 893 for cons in self.Constructors(): # only public constructors 894 minArgs = cons.minArgs 895 maxArgs = cons.maxArgs 896 # from the min number of arguments to the max number, generate 897 # all version of the given constructor 898 cons_code = '' 899 for argNum in range(minArgs, maxArgs+1): 900 params, param_names, param_types = _ParamsInfo(cons, argNum) 901 if params: 902 params = ', ' + params 903 cons_code += indent + '%s(PyObject* %s_%s):\n' % \ 904 (self.wrapper_name, self.SELF, params) 905 cons_code += indent*2 + '%s(%s), %s(%s_) {}\n\n' % \ 906 (class_name, ', '.join(param_names), self.SELF, self.SELF) 907 code += cons_code 908 # generate the body 909 body = [] 910 for method in self.virtual_methods: 911 if not self.info[method.name].exclude: 912 body.append(self.Declaration(method, indent)) 913 body = '\n'.join(body) 914 code += body + '\n' 915 # add the self member 916 code += indent + 'PyObject* %s;\n' % self.SELF 917 code += '};\n' 918 return code 919