Attachment 'ClassExporter.py'
Download 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
6 import exporters
7 from Exporter import Exporter
8 from declarations import *
9 from settings import *
10 from policies import *
11 from SingleCodeUnit import SingleCodeUnit
12 from EnumExporter import EnumExporter
13 from utils import makeid, enumerate
14 import copy
15 import exporterutils
16 import re
17
18 #==============================================================================
19 # ClassExporter
20 #==============================================================================
21 class 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 self.constructors = constructors[:]
257 # don't export constructors if the class is abstract
258 if self.class_.abstract:
259 for cons in constructors:
260 if cons.IsCopy():
261 constructors.remove(cons)
262 break
263
264 if not constructors:
265 # declare no_init
266 self.Add('constructor', py_ns + 'no_init')
267 else:
268 # write the constructor with less parameters to the constructor section
269 smaller = None
270 for cons in constructors:
271 if smaller is None or len(cons.parameters) < len(smaller.parameters):
272 smaller = cons
273 assert smaller is not None
274 self.Add('constructor', init_code(smaller))
275 constructors.remove(smaller)
276 # write the rest to the inside section, using def()
277 for cons in constructors:
278 code = '.def(%s)' % init_code(cons)
279 self.Add('inside', code)
280 # check if the class is copyable
281 if not self.class_.HasCopyConstructor() or self.class_.abstract:
282 self.Add('template', namespaces.boost + 'noncopyable')
283
284
285 def ExportVariables(self):
286 'Export the variables of the class, both static and simple variables'
287 vars = [x for x in self.public_members if isinstance(x, Variable)]
288 for var in vars:
289 if self.info[var.name].exclude:
290 continue
291 name = self.info[var.name].rename or var.name
292 fullname = var.FullName()
293 if var.type.const:
294 def_ = '.def_readonly'
295 else:
296 def_ = '.def_readwrite'
297 code = '%s("%s", &%s)' % (def_, name, fullname)
298 self.Add('inside', code)
299
300
301 def OverloadName(self, method):
302 'Returns the name of the overloads struct for the given method'
303 name = makeid(method.FullName())
304 overloads = '_overloads_%i_%i' % (method.minArgs, method.maxArgs)
305 return name + overloads
306
307
308 def GetAddedMethods(self):
309 added_methods = self.info.__added__
310 result = []
311 if added_methods:
312 for name, rename in added_methods:
313 decl = self.GetDeclaration(name)
314 self.info[name].rename = rename
315 result.append(decl)
316 return result
317
318
319 def ExportMethods(self):
320 '''Export all the non-virtual methods of this class, plus any function
321 that is to be exported as a method'''
322
323 declared = {}
324 def DeclareOverloads(m):
325 'Declares the macro for the generation of the overloads'
326 if (isinstance(m, Method) and m.static) or type(m) == Function:
327 func = m.FullName()
328 macro = 'BOOST_PYTHON_FUNCTION_OVERLOADS'
329 else:
330 func = m.name
331 macro = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS'
332 code = '%s(%s, %s, %i, %i)\n' % (macro, self.OverloadName(m), func, m.minArgs, m.maxArgs)
333 if code not in declared:
334 declared[code] = True
335 self.Add('declaration', code)
336
337
338 def Pointer(m):
339 'returns the correct pointer declaration for the method m'
340 # check if this method has a wrapper set for him
341 wrapper = self.info[m.name].wrapper
342 if wrapper:
343 return '&' + wrapper.FullName()
344 else:
345 return m.PointerDeclaration()
346
347 def IsExportable(m):
348 'Returns true if the given method is exportable by this routine'
349 ignore = (Constructor, ClassOperator, Destructor)
350 return isinstance(m, Function) and not isinstance(m, ignore) and not m.virtual
351
352 methods = [x for x in self.public_members if IsExportable(x)]
353 methods.extend(self.GetAddedMethods())
354
355 staticmethods = {}
356
357 for method in methods:
358 method_info = self.info[method.name]
359
360 # skip this method if it was excluded by the user
361 if method_info.exclude:
362 continue
363
364 # rename the method if the user requested
365 name = method_info.rename or method.name
366
367 # warn the user if this method needs a policy and doesn't have one
368 method_info.policy = exporterutils.HandlePolicy(method, method_info.policy)
369
370 # check for policies
371 policy = method_info.policy or ''
372 policy_code = ''
373 if policy:
374 policy_code = '%s%s()' % (namespaces.python, policy.Code())
375 policy = ', %s' % policy_code
376 # check for overloads
377 overload = ''
378 if method.minArgs != method.maxArgs and not method_info.wrapper:
379 # add the overloads for this method
380 DeclareOverloads(method)
381 overload_name = self.OverloadName(method)
382 overload = ', %s%s()' % (namespaces.pyste, overload_name)
383 if policy:
384 overload += '[ %s ]' % policy_code
385 policy = ''
386
387 # build the .def string to export the method
388 pointer = Pointer(method)
389 code = '.def("%s", %s' % (name, pointer)
390 code += policy
391 code += overload
392 code += ')'
393 self.Add('inside', code)
394 # static method
395 if isinstance(method, Method) and method.static:
396 staticmethods[name] = 1
397 # add wrapper code if this method has one
398 wrapper = method_info.wrapper
399 if wrapper and wrapper.code:
400 self.Add('declaration-outside', wrapper.code)
401
402 # export staticmethod statements
403 for name in staticmethods:
404 code = '.staticmethod("%s")' % name
405 self.Add('inside', code)
406
407
408
409 def MakeNonVirtual(self):
410 '''Make all methods that the user indicated to no_override no more virtual, delegating their
411 export to the ExportMethods routine'''
412 for member in self.class_:
413 if type(member) == Method and member.virtual:
414 member.virtual = not self.info[member.name].no_override
415
416
417 def ExportVirtualMethods(self, codeunit):
418 # check if this class has any virtual methods
419 has_virtual_methods = False
420 for member in self.class_:
421 if type(member) == Method and member.virtual:
422 has_virtual_methods = True
423 break
424
425 holder = self.info.holder
426 if has_virtual_methods:
427 generator = _VirtualWrapperGenerator(self.class_, self.ClassBases(), self.info, codeunit)
428 if holder:
429 self.Add('template', holder(generator.FullName()))
430 else:
431 self.Add('template', generator.FullName())
432 for definition in generator.GenerateDefinitions():
433 self.Add('inside', definition)
434 self.Add('declaration', generator.GenerateVirtualWrapper(self.INDENT))
435 else:
436 if holder:
437 self.Add('template', holder(self.class_.FullName()))
438
439 # operators natively supported by boost
440 BOOST_SUPPORTED_OPERATORS = '+ - * / % ^ & ! ~ | < > == != <= >= << >> && || += -= '\
441 '*= /= %= ^= &= |= <<= >>='.split()
442 # create a map for faster lookup
443 BOOST_SUPPORTED_OPERATORS = dict(zip(BOOST_SUPPORTED_OPERATORS, range(len(BOOST_SUPPORTED_OPERATORS))))
444
445 # a dict of operators that are not directly supported by boost, but can be exposed
446 # simply as a function with a special name
447 BOOST_RENAME_OPERATORS = {
448 '()' : '__call__',
449 }
450
451 # converters which have a special name in python
452 # it's a map of a regular expression of the converter's result to the
453 # appropriate python name
454 SPECIAL_CONVERTERS = {
455 re.compile(r'(const)?\s*double$') : '__float__',
456 re.compile(r'(const)?\s*float$') : '__float__',
457 re.compile(r'(const)?\s*int$') : '__int__',
458 re.compile(r'(const)?\s*long$') : '__long__',
459 re.compile(r'(const)?\s*char\s*\*?$') : '__str__',
460 re.compile(r'(const)?.*::basic_string<.*>\s*(\*|\&)?$') : '__str__',
461 }
462
463
464 def ExportOperators(self):
465 'Export all member operators and free operators related to this class'
466
467 def GetFreeOperators():
468 'Get all the free (global) operators related to this class'
469 operators = []
470 for decl in self.declarations:
471 if isinstance(decl, Operator):
472 # check if one of the params is this class
473 for param in decl.parameters:
474 if param.name == self.class_.FullName():
475 operators.append(decl)
476 break
477 return operators
478
479 def GetOperand(param):
480 'Returns the operand of this parameter (either "self", or "other<type>")'
481 if param.name == self.class_.FullName():
482 return namespaces.python + 'self'
483 else:
484 return namespaces.python + ('other< %s >()' % param.name)
485
486
487 def HandleSpecialOperator(operator):
488 # gatter information about the operator and its parameters
489 result_name = operator.result.name
490 param1_name = ''
491 if operator.parameters:
492 param1_name = operator.parameters[0].name
493
494 # check for str
495 ostream = 'basic_ostream'
496 is_str = result_name.find(ostream) != -1 and param1_name.find(ostream) != -1
497 if is_str:
498 namespace = namespaces.python + 'self_ns::'
499 self_ = namespaces.python + 'self'
500 return '.def(%sstr(%s))' % (namespace, self_)
501
502 # is not a special operator
503 return None
504
505
506
507 frees = GetFreeOperators()
508 members = [x for x in self.public_members if type(x) == ClassOperator]
509 all_operators = frees + members
510 operators = [x for x in all_operators if not self.info['operator'][x.name].exclude]
511
512 for operator in operators:
513 # gatter information about the operator, for use later
514 wrapper = self.info['operator'][operator.name].wrapper
515 if wrapper:
516 pointer = '&' + wrapper.FullName()
517 if wrapper.code:
518 self.Add('declaration-outside', wrapper.code)
519 else:
520 pointer = operator.PointerDeclaration()
521 rename = self.info['operator'][operator.name].rename
522
523 # check if this operator will be exported as a method
524 export_as_method = wrapper or rename or operator.name in self.BOOST_RENAME_OPERATORS
525
526 # check if this operator has a special representation in boost
527 special_code = HandleSpecialOperator(operator)
528 has_special_representation = special_code is not None
529
530 if export_as_method:
531 # export this operator as a normal method, renaming or using the given wrapper
532 if not rename:
533 if wrapper:
534 rename = wrapper.name
535 else:
536 rename = self.BOOST_RENAME_OPERATORS[operator.name]
537 policy = ''
538 policy_obj = self.info['operator'][operator.name].policy
539 if policy_obj:
540 policy = ', %s()' % policy_obj.Code()
541 self.Add('inside', '.def("%s", %s%s)' % (rename, pointer, policy))
542
543 elif has_special_representation:
544 self.Add('inside', special_code)
545
546 elif operator.name in self.BOOST_SUPPORTED_OPERATORS:
547 # export this operator using boost's facilities
548 op = operator
549 is_unary = isinstance(op, Operator) and len(op.parameters) == 1 or\
550 isinstance(op, ClassOperator) and len(op.parameters) == 0
551 if is_unary:
552 self.Add('inside', '.def( %s%sself )' % \
553 (operator.name, namespaces.python))
554 else:
555 # binary operator
556 if len(operator.parameters) == 2:
557 left_operand = GetOperand(operator.parameters[0])
558 right_operand = GetOperand(operator.parameters[1])
559 else:
560 left_operand = namespaces.python + 'self'
561 right_operand = GetOperand(operator.parameters[0])
562 self.Add('inside', '.def( %s %s %s )' % \
563 (left_operand, operator.name, right_operand))
564
565 # export the converters.
566 # export them as simple functions with a pre-determined name
567
568 converters = [x for x in self.public_members if type(x) == ConverterOperator]
569
570 def ConverterMethodName(converter):
571 result_fullname = converter.result.FullName()
572 result_name = converter.result.name
573 for regex, method_name in self.SPECIAL_CONVERTERS.items():
574 if regex.match(result_fullname):
575 return method_name
576 else:
577 # extract the last name from the full name
578 result_name = makeid(result_name)
579 return 'to_' + result_name
580
581 for converter in converters:
582 info = self.info['operator'][converter.result.FullName()]
583 # check if this operator should be excluded
584 if info.exclude:
585 continue
586
587 special_code = HandleSpecialOperator(converter)
588 if info.rename or not special_code:
589 # export as method
590 name = info.rename or ConverterMethodName(converter)
591 pointer = converter.PointerDeclaration()
592 policy_code = ''
593 if info.policy:
594 policy_code = ', %s()' % info.policy.Code()
595 self.Add('inside', '.def("%s", %s%s)' % (name, pointer, policy_code))
596
597 elif special_code:
598 self.Add('inside', special_code)
599
600
601
602 def ExportNestedClasses(self, exported_names):
603 nested_classes = [x for x in self.public_members if isinstance(x, NestedClass)]
604 for nested_class in nested_classes:
605 nested_info = self.info[nested_class.name]
606 nested_info.include = self.info.include
607 nested_info.name = nested_class.FullName()
608 exporter = self.__class__(nested_info)
609 exporter.SetDeclarations(self.declarations)
610 codeunit = SingleCodeUnit(None, None)
611 exporter.Export(codeunit, exported_names)
612 self.nested_codeunits.append(codeunit)
613
614
615 def ExportNestedEnums(self, exported_names):
616 nested_enums = [x for x in self.public_members if isinstance(x, ClassEnumeration)]
617 for enum in nested_enums:
618 enum_info = self.info[enum.name]
619 enum_info.include = self.info.include
620 enum_info.name = enum.FullName()
621 exporter = EnumExporter(enum_info)
622 exporter.SetDeclarations(self.declarations)
623 codeunit = SingleCodeUnit(None, None)
624 exporter.Export(codeunit, exported_names)
625 self.nested_codeunits.append(codeunit)
626
627
628 def ExportSmartPointer(self):
629 smart_ptr = self.info.smart_ptr
630 if smart_ptr:
631 class_name = self.class_.FullName()
632 smart_ptr = smart_ptr % class_name
633 self.Add('scope', '%sregister_ptr_to_python< %s >();' % (namespaces.python, smart_ptr))
634
635
636 def ExportOpaquePointerPolicies(self):
637 # check all methods for 'return_opaque_pointer' policies
638 methods = [x for x in self.public_members if isinstance(x, Method)]
639 for method in methods:
640 return_opaque_policy = return_value_policy(return_opaque_pointer)
641 if self.info[method.name].policy == return_opaque_policy:
642 macro = exporterutils.EspecializeTypeID(method.result.name)
643 if macro:
644 self.Add('declaration-outside', macro)
645
646 def ExportAddedCode(self):
647 if self.info.__code__:
648 for code in self.info.__code__:
649 self.Add('inside', code)
650
651
652 #==============================================================================
653 # Virtual Wrapper utils
654 #==============================================================================
655
656 def _ParamsInfo(m, count=None):
657 if count is None:
658 count = len(m.parameters)
659 param_names = ['p%i' % i for i in range(count)]
660 param_types = [x.FullName() for x in m.parameters[:count]]
661 params = ['%s %s' % (t, n) for t, n in zip(param_types, param_names)]
662 #for i, p in enumerate(m.parameters[:count]):
663 # if p.default is not None:
664 # #params[i] += '=%s' % p.default
665 # params[i] += '=%s' % (p.name + '()')
666 params = ', '.join(params)
667 return params, param_names, param_types
668
669
670 class _VirtualWrapperGenerator(object):
671 'Generates code to export the virtual methods of the given class'
672
673 def __init__(self, class_, bases, info, codeunit):
674 self.class_ = copy.deepcopy(class_)
675 self.bases = bases[:]
676 self.info = info
677 self.wrapper_name = makeid(class_.FullName()) + '_Wrapper'
678 self.virtual_methods = None
679 self._method_count = {}
680 self.codeunit = codeunit
681 self.GenerateVirtualMethods()
682
683
684 SELF = 'py_self'
685
686
687 def DefaultImplementationNames(self, method):
688 '''Returns a list of default implementations for this method, one for each
689 number of default arguments. Always returns at least one name, and return from
690 the one with most arguments to the one with the least.
691 '''
692 base_name = 'default_' + method.name
693 minArgs = method.minArgs
694 maxArgs = method.maxArgs
695 if minArgs == maxArgs:
696 return [base_name]
697 else:
698 return [base_name + ('_%i' % i) for i in range(minArgs, maxArgs+1)]
699
700
701 def Declaration(self, method, indent):
702 '''Returns a string with the declarations of the virtual wrapper and
703 its default implementations. This string must be put inside the Wrapper
704 body.
705 '''
706 pyste = namespaces.pyste
707 python = namespaces.python
708 rename = self.info[method.name].rename or method.name
709 result = method.result.FullName()
710 return_str = 'return '
711 if result == 'void':
712 return_str = ''
713 params, param_names, param_types = _ParamsInfo(method)
714 constantness = ''
715 if method.const:
716 constantness = ' const'
717
718 # call_method callback
719 decl = indent + '%s %s(%s)%s%s {\n' % (result, method.name, params, constantness, method.Exceptions())
720 param_names_str = ', '.join(param_names)
721 if param_names_str:
722 param_names_str = ', ' + param_names_str
723
724 self_str = self.SELF
725
726 decl += indent*2 + '%(return_str)s%(python)scall_method< %(result)s >' \
727 '(%(self_str)s, "%(rename)s"%(param_names_str)s);\n' % locals()
728 decl += indent + '}\n'
729
730 # default implementations (with overloading)
731 def DefaultImpl(method, param_names):
732 'Return the body of a default implementation wrapper'
733 indent2 = indent * 2
734 wrapper = self.info[method.name].wrapper
735 if not wrapper:
736 # return the default implementation of the class
737 return indent2 + '%s%s(%s);\n' % \
738 (return_str, method.FullName(), ', '.join(param_names))
739 else:
740 if wrapper.code:
741 self.codeunit.Write('declaration-outside', wrapper.code)
742 # return a call for the wrapper
743 params = ', '.join(['this'] + param_names)
744 return indent2 + '%s%s(%s);\n' % (return_str, wrapper.FullName(), params)
745
746 if not method.abstract and method.visibility != Scope.private:
747 minArgs = method.minArgs
748 maxArgs = method.maxArgs
749 impl_names = self.DefaultImplementationNames(method)
750 for impl_name, argNum in zip(impl_names, range(minArgs, maxArgs+1)):
751 params, param_names, param_types = _ParamsInfo(method, argNum)
752 decl += '\n'
753 decl += indent + '%s %s(%s)%s {\n' % (result, impl_name, params, constantness)
754 decl += DefaultImpl(method, param_names)
755 decl += indent + '}\n'
756 return decl
757
758
759 def MethodDefinition(self, method):
760 '''Returns a list of lines, which should be put inside the class_
761 statement to export this method.'''
762 # dont define abstract methods
763 pyste = namespaces.pyste
764 rename = self.info[method.name].rename or method.name
765 default_names = self.DefaultImplementationNames(method)
766 class_name = self.class_.FullName()
767 wrapper_name = pyste + self.wrapper_name
768 result = method.result.FullName()
769 is_method_unique = method.is_unique
770 constantness = ''
771 if method.const:
772 constantness = ' const'
773
774 # create a list of default-impl pointers
775 minArgs = method.minArgs
776 maxArgs = method.maxArgs
777 if method.abstract:
778 default_pointers = []
779 elif is_method_unique:
780 default_pointers = ['&%s::%s' % (wrapper_name, x) for x in default_names]
781 else:
782 default_pointers = []
783 for impl_name, argNum in zip(default_names, range(minArgs, maxArgs+1)):
784 param_list = [x.FullName() for x in method.parameters[:argNum]]
785 params = ', '.join(param_list)
786 signature = '%s (%s::*)(%s)%s' % (result, wrapper_name, params, constantness)
787 default_pointer = '(%s)&%s::%s' % (signature, wrapper_name, impl_name)
788 default_pointers.append(default_pointer)
789
790 # get the pointer of the method
791 pointer = method.PointerDeclaration()
792 if method.abstract:
793 pointer = namespaces.python + ('pure_virtual(%s)' % pointer)
794
795 # warn the user if this method needs a policy and doesn't have one
796 method_info = self.info[method.name]
797 method_info.policy = exporterutils.HandlePolicy(method, method_info.policy)
798
799 # Add policy to overloaded methods also
800 policy = method_info.policy or ''
801 if policy:
802 policy = ', %s%s()' % (namespaces.python, policy.Code())
803
804 # generate the defs
805 definitions = []
806 # basic def
807 if default_pointers:
808 definitions.append('.def("%s", %s, %s%s)' % (rename, pointer, default_pointers[-1], policy))
809 for default_pointer in default_pointers[:-1]:
810 definitions.append('.def("%s", %s%s)' % (rename, default_pointer, policy))
811 else:
812 definitions.append('.def("%s", %s%s)' % (rename, pointer, policy))
813 return definitions
814
815
816 def FullName(self):
817 return namespaces.pyste + self.wrapper_name
818
819
820 def GenerateVirtualMethods(self):
821 '''To correctly export all virtual methods, we must also make wrappers
822 for the virtual methods of the bases of this class, as if the methods
823 were from this class itself.
824 This method creates the instance variable self.virtual_methods.
825 '''
826 def IsVirtual(m):
827 if type(m) is Method:
828 pure_virtual = m.abstract and m.virtual
829 virtual = m.virtual and m.visibility != Scope.private
830 return virtual or pure_virtual
831 else:
832 return False
833
834 # extract the virtual methods, avoiding duplications. The duplication
835 # must take in account the full signature without the class name, so
836 # that inherited members are correctly excluded if the subclass overrides
837 # them.
838 def MethodSig(method):
839 if method.const:
840 const = ' const'
841 else:
842 const = ''
843 if method.result:
844 result = method.result.FullName()
845 else:
846 result = ''
847 params = ', '.join([x.FullName() for x in method.parameters])
848 return '%s %s(%s)%s%s' % (
849 result, method.name, params, const, method.Exceptions())
850
851 already_added = {}
852 self.virtual_methods = []
853 for member in self.class_:
854 if IsVirtual(member):
855 already_added[MethodSig(member)] = None
856 self.virtual_methods.append(member)
857
858 for base in self.bases:
859 base_methods = [copy.deepcopy(x) for x in base if IsVirtual(x)]
860 for base_method in base_methods:
861 self.class_.AddMember(base_method)
862
863 all_methods = [x for x in self.class_ if IsVirtual(x)]
864
865 for member in all_methods:
866 sig = MethodSig(member)
867 if IsVirtual(member) and not sig in already_added:
868 self.virtual_methods.append(member)
869 already_added[sig] = 0
870
871
872 def Constructors(self):
873 return self.class_.Constructors(publics_only=True)
874
875
876 def GenerateDefinitions(self):
877 defs = []
878 for method in self.virtual_methods:
879 exclude = self.info[method.name].exclude
880 # generate definitions only for public methods and non-abstract methods
881 if method.visibility == Scope.public and not exclude:
882 defs.extend(self.MethodDefinition(method))
883 return defs
884
885
886 def GenerateVirtualWrapper(self, indent):
887 'Return the wrapper for this class'
888
889 # generate the class code
890 class_name = self.class_.FullName()
891 code = 'struct %s: %s\n' % (self.wrapper_name, class_name)
892 code += '{\n'
893 # generate constructors (with the overloads for each one)
894 for cons in self.Constructors(): # only public constructors
895 minArgs = cons.minArgs
896 maxArgs = cons.maxArgs
897 # from the min number of arguments to the max number, generate
898 # all version of the given constructor
899 cons_code = ''
900 for argNum in range(minArgs, maxArgs+1):
901 params, param_names, param_types = _ParamsInfo(cons, argNum)
902 if params:
903 params = ', ' + params
904 cons_code += indent + '%s(PyObject* %s_%s):\n' % \
905 (self.wrapper_name, self.SELF, params)
906 cons_code += indent*2 + '%s(%s), %s(%s_) {}\n\n' % \
907 (class_name, ', '.join(param_names), self.SELF, self.SELF)
908 code += cons_code
909 # generate the body
910 body = []
911 for method in self.virtual_methods:
912 if not self.info[method.name].exclude:
913 body.append(self.Declaration(method, indent))
914 body = '\n'.join(body)
915 code += body + '\n'
916 # add the self member
917 code += indent + 'PyObject* %s;\n' % self.SELF
918 code += '};\n'
919 return code
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.- [get | view] (2011-10-26 21:54:24, 37.3 KB) [[attachment:ClassExporter.py]]
- [get | view] (2011-10-26 21:54:24, 16.1 KB) [[attachment:GCCXMLParser.py]]
- [get | view] (2011-10-26 21:54:24, 91.1 KB) [[attachment:sphere_cylinder_dist_grey_c.png]]
You are not allowed to attach a file to this page.