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