mirror of
https://github.com/boostorg/python.git
synced 2026-01-20 16:52:15 +00:00
- fixed default arguments in virtual methods
[SVN r17823]
This commit is contained in:
@@ -34,12 +34,9 @@ class ClassExporter(Exporter):
|
||||
# declarations: outside the BOOST_PYTHON_MODULE macro
|
||||
self.sections['declaration'] = []
|
||||
self.sections['include'] = []
|
||||
# a list of Method instances
|
||||
self.methods = []
|
||||
# a list of Constructor instances
|
||||
self.constructors = []
|
||||
# a dict of methodname => _WrapperVirtualMethod instances
|
||||
self.virtual_wrappers = {}
|
||||
self.wrapper_generator = None
|
||||
# a list of code units, generated by nested declarations
|
||||
self.nested_codeunits = []
|
||||
|
||||
@@ -83,13 +80,12 @@ class ClassExporter(Exporter):
|
||||
|
||||
|
||||
def Export(self, codeunit, exported_names):
|
||||
self.GetMethods()
|
||||
self.ExportBasics()
|
||||
self.ExportBases(exported_names)
|
||||
self.ExportConstructors()
|
||||
self.ExportVariables()
|
||||
self.ExportMethods()
|
||||
self.GenerateVirtualWrapper()
|
||||
self.ExportVirtualMethods()
|
||||
self.ExportOperators()
|
||||
self.ExportNestedClasses(exported_names)
|
||||
self.ExportNestedEnums()
|
||||
@@ -151,7 +147,7 @@ class ClassExporter(Exporter):
|
||||
|
||||
def Add(self, section, item):
|
||||
'Add the item into the corresponding section'
|
||||
self.sections[section].append(item.strip())
|
||||
self.sections[section].append(item)
|
||||
|
||||
|
||||
def ExportBasics(self):
|
||||
@@ -203,8 +199,14 @@ class ClassExporter(Exporter):
|
||||
# declare no_init
|
||||
self.Add('constructor', py_ns + 'no_init')
|
||||
else:
|
||||
# write one of the constructors to the class_ constructor
|
||||
self.Add('constructor', init_code(constructors.pop(0)))
|
||||
# write the constructor with less parameters to the constructor section
|
||||
smaller = None
|
||||
for cons in constructors:
|
||||
if smaller is None or len(cons.parameters) < len(smaller.parameters):
|
||||
smaller = cons
|
||||
assert smaller is not None
|
||||
self.Add('constructor', init_code(smaller))
|
||||
constructors.remove(smaller)
|
||||
# write the rest to the inside section, using def()
|
||||
for cons in constructors:
|
||||
code = '.def(%s)' % init_code(cons)
|
||||
@@ -234,18 +236,6 @@ class ClassExporter(Exporter):
|
||||
self.Add('inside', code)
|
||||
|
||||
|
||||
def GetMethods(self):
|
||||
'fill self.methods with a list of Method instances'
|
||||
# get a list of all methods
|
||||
def IsValid(m):
|
||||
'Returns true if the given method is exportable by this routine'
|
||||
ignore = (Constructor, ClassOperator, Destructor)
|
||||
return isinstance(m, Method) and not isinstance(m, ignore)
|
||||
|
||||
self.methods = [x for x in self.public_members if IsValid(x)]
|
||||
|
||||
|
||||
|
||||
printed_policy_warnings = {}
|
||||
|
||||
def CheckPolicy(self, m):
|
||||
@@ -265,25 +255,22 @@ class ClassExporter(Exporter):
|
||||
|
||||
|
||||
def ExportMethods(self):
|
||||
'Export all the methods of the class'
|
||||
'Export all the non-virtual methods of this class'
|
||||
|
||||
def OverloadName(m):
|
||||
'Returns the name of the overloads struct for the given method'
|
||||
|
||||
'Returns the name of the overloads struct for the given method'
|
||||
return _ID(m.FullName()) + ('_overloads_%i_%i' % (m.minArgs, m.maxArgs))
|
||||
|
||||
declared = {}
|
||||
def DeclareOverloads(m):
|
||||
'Declares the macro for the generation of the overloads'
|
||||
if m.virtual:
|
||||
func = self.virtual_wrappers[m.PointerDeclaration()].DefaultName()
|
||||
else:
|
||||
if not m.virtual:
|
||||
func = m.name
|
||||
code = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(%s, %s, %i, %i)\n'
|
||||
code = code % (OverloadName(m), func, m.minArgs, m.maxArgs)
|
||||
if code not in declared:
|
||||
declared[code] = True
|
||||
self.Add('declaration', code)
|
||||
code = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(%s, %s, %i, %i)\n'
|
||||
code = code % (OverloadName(m), func, m.minArgs, m.maxArgs)
|
||||
if code not in declared:
|
||||
declared[code] = True
|
||||
self.Add('declaration', code)
|
||||
|
||||
|
||||
def Pointer(m):
|
||||
@@ -292,9 +279,6 @@ class ClassExporter(Exporter):
|
||||
wrapper = self.info[method.name].wrapper
|
||||
if wrapper:
|
||||
return '&' + wrapper.FullName()
|
||||
# if this method is virtual, return the pointers to the class and its wrapper
|
||||
if m.virtual:
|
||||
return self.virtual_wrappers[m.PointerDeclaration()].Pointer()
|
||||
# return normal pointers to the methods of the class
|
||||
is_unique = self.class_.IsUnique(m.name)
|
||||
if is_unique:
|
||||
@@ -302,19 +286,19 @@ class ClassExporter(Exporter):
|
||||
else:
|
||||
return method.PointerDeclaration()
|
||||
|
||||
|
||||
for method in self.methods:
|
||||
def IsExportable(m):
|
||||
'Returns true if the given method is exportable by this routine'
|
||||
ignore = (Constructor, ClassOperator, Destructor)
|
||||
return isinstance(m, Method) and not isinstance(m, ignore) and not m.virtual
|
||||
|
||||
methods = [x for x in self.public_members if IsExportable(x)]
|
||||
|
||||
for method in methods:
|
||||
if self.info[method.name].exclude:
|
||||
continue # skip this method
|
||||
|
||||
name = self.info[method.name].rename or method.name
|
||||
# check if this method needs to be wrapped as a virtual method
|
||||
if method.virtual:
|
||||
wrapper = _WrapperVirtualMethod(self.class_, method, name)
|
||||
self.virtual_wrappers[method.PointerDeclaration()] = wrapper
|
||||
# abstract methods don't need to be exported
|
||||
if method.abstract:
|
||||
continue # skip .def declaration for abstract methods
|
||||
|
||||
# warn the user if this method needs a policy and doesn't have one
|
||||
self.CheckPolicy(method)
|
||||
|
||||
@@ -328,15 +312,9 @@ class ClassExporter(Exporter):
|
||||
# add the overloads for this method
|
||||
overload_name = OverloadName(method)
|
||||
DeclareOverloads(method)
|
||||
if not method.virtual:
|
||||
overload = ', %s%s()' % (namespaces.pyste, overload_name)
|
||||
else:
|
||||
pyste_ns = namespaces.pyste
|
||||
pointer = self.virtual_wrappers[method.PointerDeclaration()].DefaultPointer()
|
||||
defcode = '.def("%s", %s, %s%s())' % \
|
||||
(name, pointer, pyste_ns, overload_name)
|
||||
self.Add('inside', defcode)
|
||||
# build the string to export the method
|
||||
overload = ', %s%s()' % (namespaces.pyste, overload_name)
|
||||
|
||||
# build the .def string to export the method
|
||||
pointer = Pointer(method)
|
||||
code = '.def("%s", %s' % (name, pointer)
|
||||
code += policy
|
||||
@@ -353,38 +331,21 @@ class ClassExporter(Exporter):
|
||||
self.Add('declaration', wrapper.code)
|
||||
|
||||
|
||||
def GenerateVirtualWrapper(self):
|
||||
'Generate the wrapper to dispatch virtual methods'
|
||||
# check if this class needs a wrapper first
|
||||
for m in self.methods:
|
||||
if m.virtual:
|
||||
def ExportVirtualMethods(self):
|
||||
# check if this class has any virtual methods
|
||||
has_virtual_methods = False
|
||||
for member in self.class_.members:
|
||||
if type(member) == Method and member.virtual:
|
||||
has_virtual_methods = True
|
||||
break
|
||||
else:
|
||||
return
|
||||
# add the wrapper name to the template section
|
||||
wrapper_name = _WrapperName(self.class_)
|
||||
self.Add('template', namespaces.pyste + wrapper_name)
|
||||
indent = self.INDENT
|
||||
method_codes = [x.Code(indent) for x in self.virtual_wrappers.values()]
|
||||
body = '\n'.join(method_codes)
|
||||
# generate the class code
|
||||
class_name = self.class_.FullName()
|
||||
code = 'struct %s: %s\n' % (wrapper_name, class_name)
|
||||
code += '{\n'
|
||||
# generate constructors
|
||||
for cons in self.constructors:
|
||||
params, param_names, param_types = _ParamsInfo(cons)
|
||||
if params:
|
||||
params = ', ' + params
|
||||
cons_code = indent + '%s(PyObject* self_%s):\n' % (wrapper_name, params)
|
||||
cons_code += indent*2 + '%s(%s), self(self_) {}\n\n' % \
|
||||
(class_name, ', '.join(param_names))
|
||||
code += cons_code
|
||||
code += body + '\n'
|
||||
code += indent + 'PyObject* self;\n'
|
||||
code += '};\n'
|
||||
self.Add('declaration', code)
|
||||
|
||||
if has_virtual_methods:
|
||||
generator = _VirtualWrapperGenerator(self.class_, self.info)
|
||||
self.Add('template', generator.FullName())
|
||||
for definition in generator.GenerateDefinitions():
|
||||
self.Add('inside', definition)
|
||||
self.Add('declaration', generator.GenerateVirtualWrapper(self.INDENT))
|
||||
|
||||
|
||||
# operators natively supported by boost
|
||||
BOOST_SUPPORTED_OPERATORS = '+ - * / % ^ & ! ~ | < > == != <= >= << >> && || += -='\
|
||||
@@ -585,104 +546,181 @@ def _ID(name):
|
||||
# Virtual Wrapper utils
|
||||
#==============================================================================
|
||||
|
||||
def _WrapperName(class_):
|
||||
return _ID(class_.FullName()) + '_Wrapper'
|
||||
|
||||
|
||||
def _ParamsInfo(m):
|
||||
param_names = ['p%i' % i for i in range(len(m.parameters))]
|
||||
param_types = [x.FullName() for x in m.parameters]
|
||||
def _ParamsInfo(m, count=None):
|
||||
if count is None:
|
||||
count = len(m.parameters)
|
||||
param_names = ['p%i' % i for i in range(count)]
|
||||
param_types = [x.FullName() for x in m.parameters[:count]]
|
||||
params = ['%s %s' % (t, n) for t, n in zip(param_types, param_names)]
|
||||
for i, p in enumerate(m.parameters):
|
||||
if p.default is not None:
|
||||
#params[i] += '=%s' % p.default
|
||||
params[i] += '=%s' % (p.name + '()')
|
||||
#for i, p in enumerate(m.parameters[:count]):
|
||||
# if p.default is not None:
|
||||
# #params[i] += '=%s' % p.default
|
||||
# params[i] += '=%s' % (p.name + '()')
|
||||
params = ', '.join(params)
|
||||
return params, param_names, param_types
|
||||
|
||||
|
||||
class _WrapperVirtualMethod(object):
|
||||
'Holds information about a virtual method that will be wrapped'
|
||||
class _VirtualWrapperGenerator(object):
|
||||
'Generates code to export the virtual methods of the given class'
|
||||
|
||||
def __init__(self, class_, method, rename):
|
||||
self.method = method
|
||||
if rename is None:
|
||||
rename = method.name
|
||||
self.rename = rename
|
||||
def __init__(self, class_, info):
|
||||
self.class_ = class_
|
||||
self.info = info
|
||||
self.wrapper_name = _ID(class_.FullName()) + '_Wrapper'
|
||||
|
||||
|
||||
def DefaultName(self):
|
||||
return 'default_' + self.method.name
|
||||
|
||||
|
||||
def DefaultPointer(self):
|
||||
ns = namespaces.pyste
|
||||
wrapper_name = _WrapperName(self.class_)
|
||||
default_name = self.DefaultName()
|
||||
fullname = '%s%s::%s' % (ns, wrapper_name, default_name)
|
||||
if self.class_.IsUnique(self.method.name):
|
||||
return '&%s' % fullname
|
||||
def DefaultImplementationNames(self, method):
|
||||
'''Returns a list of default implementations for this method, one for each
|
||||
number of default arguments. Always returns at least one name, and return from
|
||||
the one with most arguments to the one with the least.
|
||||
'''
|
||||
base_name = 'default_' + method.name
|
||||
minArgs = method.minArgs
|
||||
maxArgs = method.maxArgs
|
||||
if minArgs == maxArgs:
|
||||
return [base_name]
|
||||
else:
|
||||
# the method is not unique, so we must specify the entire signature with it
|
||||
param_list = [x.FullName() for x in self.method.parameters]
|
||||
params = ', '.join(param_list)
|
||||
result = self.method.result.FullName()
|
||||
signature = '%s (%s%s::*)(%s)' % (result, ns, wrapper_name, params)
|
||||
return '(%s)%s' % (signature, fullname)
|
||||
return [base_name + ('_%i' % i) for i in range(minArgs, maxArgs+1)]
|
||||
|
||||
|
||||
def Declaration(self, method, indent):
|
||||
'''Returns a string with the declarations of the virtual wrapper and
|
||||
its default implementations. This string must be put inside the Wrapper
|
||||
body.
|
||||
'''
|
||||
pyste = namespaces.pyste
|
||||
python = namespaces.python
|
||||
rename = self.info[method.name].rename or method.name
|
||||
result = method.result.FullName()
|
||||
return_str = 'return '
|
||||
if result == 'void':
|
||||
return_str = ''
|
||||
params, param_names, param_types = _ParamsInfo(method)
|
||||
constantness = ''
|
||||
if method.const:
|
||||
constantness = ' const'
|
||||
|
||||
# call_method callback
|
||||
decl = indent + '%s %s(%s)%s {\n' % (result, method.name, params, constantness)
|
||||
param_names_str = ', '.join(param_names)
|
||||
if param_names_str:
|
||||
param_names_str = ', ' + param_names_str
|
||||
decl += indent*2 + '%s%scall_method<%s>(self, "%s"%s);\n' %\
|
||||
(return_str, python, result, rename, param_names_str)
|
||||
decl += indent + '}\n'
|
||||
|
||||
# default implementations (with overloading)
|
||||
if not method.abstract:
|
||||
minArgs = method.minArgs
|
||||
maxArgs = method.maxArgs
|
||||
impl_names = self.DefaultImplementationNames(method)
|
||||
for impl_name, argNum in zip(impl_names, range(minArgs, maxArgs+1)):
|
||||
params, param_names, param_types = _ParamsInfo(method, argNum)
|
||||
decl += '\n'
|
||||
decl += indent + '%s %s(%s)%s {\n' % (result, impl_name, params, constantness)
|
||||
decl += indent*2 + '%s%s::%s(%s);\n' % \
|
||||
(return_str, self.class_.FullName(), method.name, ', '.join(param_names))
|
||||
decl += indent + '}\n'
|
||||
return decl
|
||||
|
||||
|
||||
def Pointer(self):
|
||||
'''Returns the "pointer" declaration for this method, ie, the contents
|
||||
of the .def after the method name (.def("name", <pointer>))'''
|
||||
ns = namespaces.pyste
|
||||
default_name = self.DefaultName()
|
||||
name = self.method.name
|
||||
def MethodDefinition(self, method):
|
||||
'''Returns a list of lines, which should be put inside the class_
|
||||
statement to export this method.'''
|
||||
# dont define abstract methods
|
||||
if method.abstract:
|
||||
return []
|
||||
pyste = namespaces.pyste
|
||||
rename = self.info[method.name].rename or method.name
|
||||
default_names = self.DefaultImplementationNames(method)
|
||||
class_name = self.class_.FullName()
|
||||
wrapper = ns + _WrapperName(self.class_)
|
||||
if self.class_.IsUnique(self.method.name):
|
||||
return '&%s::%s, &%s::%s' % (class_name, name, wrapper, default_name)
|
||||
wrapper_name = pyste + self.wrapper_name
|
||||
result = method.result.FullName()
|
||||
is_method_unique = self.class_.IsUnique(method.name)
|
||||
constantness = ''
|
||||
if method.const:
|
||||
constantness = ' const'
|
||||
|
||||
# create a list of default-impl pointers
|
||||
minArgs = method.minArgs
|
||||
maxArgs = method.maxArgs
|
||||
if is_method_unique:
|
||||
default_pointers = ['&%s::%s' % (wrapper_name, x) for x in default_names]
|
||||
else:
|
||||
# the method is not unique, so we must specify the entire signature with it
|
||||
param_list = [x.FullName() for x in self.method.parameters]
|
||||
params = ', '.join(param_list)
|
||||
result = self.method.result.FullName()
|
||||
default_sig = '%s (%s::*)(%s)' % (result, wrapper, params)
|
||||
normal_sig = '%s (%s::*)(%s)' % (result, class_name, params)
|
||||
return '(%s)%s::%s, (%s)%s::%s' % \
|
||||
(normal_sig, class_name, name, default_sig, wrapper, default_name)
|
||||
default_pointers = []
|
||||
for impl_name, argNum in zip(default_names, range(minArgs, maxArgs+1)):
|
||||
param_list = [x.FullName() for x in method.parameters[:argNum]]
|
||||
params = ', '.join(param_list)
|
||||
signature = '%s (%s::*)(%s)%s' % (result, wrapper_name, params, constantness)
|
||||
default_pointer = '(%s)%s::%s' % (signature, wrapper_name, impl_name)
|
||||
default_pointers.append(default_pointer)
|
||||
|
||||
# get the pointer of the method
|
||||
if is_method_unique:
|
||||
pointer = '&' + method.FullName()
|
||||
else:
|
||||
pointer = method.PointerDeclaration()
|
||||
|
||||
# generate the defs
|
||||
definitions = []
|
||||
# basic def
|
||||
definitions.append('.def("%s", %s, %s)' % (rename, pointer, default_pointers[-1]))
|
||||
for default_pointer in default_pointers[:-1]:
|
||||
definitions.append('.def("%s", %s)' % (rename, default_pointer))
|
||||
return definitions
|
||||
|
||||
def Code(self, indent):
|
||||
params, param_names, param_types = _ParamsInfo(self.method)
|
||||
result = self.method.result.FullName()
|
||||
return_ = 'return '
|
||||
if result == 'void':
|
||||
return_ = ''
|
||||
param_names = ', '.join(param_names)
|
||||
class_name = self.class_.FullName()
|
||||
method_name = self.method.name
|
||||
default_name = self.DefaultName()
|
||||
# constantness
|
||||
const = ''
|
||||
if self.method.const:
|
||||
const = 'const '
|
||||
code = ''
|
||||
# create default_method if this method has a default implementation
|
||||
if not self.method.abstract:
|
||||
default_sig = '%s %s(%s) %s' % (result, default_name, params, const)
|
||||
body = '{ %s%s::%s(%s); } ' % \
|
||||
(return_, class_name, method_name, param_names)
|
||||
code += indent + default_sig + body + '\n'
|
||||
# create normal method
|
||||
normal_sig = '%s %s(%s) %s' % (result, method_name, params, const)
|
||||
if param_names:
|
||||
param_names = ', ' + param_names
|
||||
body = '{ %s%scall_method< %s >(self, "%s"%s); }' % \
|
||||
(return_, namespaces.python, result, self.rename, param_names)
|
||||
code += indent + normal_sig + body + '\n'
|
||||
|
||||
return code
|
||||
|
||||
|
||||
|
||||
def FullName(self):
|
||||
return namespaces.pyste + self.wrapper_name
|
||||
|
||||
|
||||
def VirtualMethods(self):
|
||||
return [m for m in self.class_.members if type(m) == Method and m.virtual]
|
||||
|
||||
|
||||
def Constructors(self):
|
||||
return [m for m in self.class_.members if isinstance(m, Constructor)]
|
||||
|
||||
|
||||
def GenerateDefinitions(self):
|
||||
defs = []
|
||||
for method in self.VirtualMethods():
|
||||
if not self.info[method.name].exclude:
|
||||
defs.extend(self.MethodDefinition(method))
|
||||
return defs
|
||||
|
||||
|
||||
def GenerateVirtualWrapper(self, indent):
|
||||
'Return the wrapper for this class'
|
||||
|
||||
# generate the class code
|
||||
class_name = self.class_.FullName()
|
||||
code = 'struct %s: %s\n' % (self.wrapper_name, class_name)
|
||||
code += '{\n'
|
||||
# generate constructors (with the overloads for each one)
|
||||
for cons in self.Constructors():
|
||||
minArgs = cons.minArgs
|
||||
maxArgs = cons.maxArgs
|
||||
# from the min number of arguments to the max number, generate
|
||||
# all version of the given constructor
|
||||
cons_code = ''
|
||||
for argNum in range(minArgs, maxArgs+1):
|
||||
params, param_names, param_types = _ParamsInfo(cons, argNum)
|
||||
if params:
|
||||
params = ', ' + params
|
||||
cons_code += indent + '%s(PyObject* self_%s):\n' % \
|
||||
(self.wrapper_name, params)
|
||||
cons_code += indent*2 + '%s(%s), self(self_) {}\n\n' % \
|
||||
(class_name, ', '.join(param_names))
|
||||
code += cons_code
|
||||
# generate the body
|
||||
body = []
|
||||
for method in self.VirtualMethods():
|
||||
if not self.info[method.name].exclude:
|
||||
body.append(self.Declaration(method, indent))
|
||||
body = '\n'.join(body)
|
||||
code += body + '\n'
|
||||
# add the self member
|
||||
code += indent + 'PyObject* self;\n'
|
||||
code += '};\n'
|
||||
return code
|
||||
|
||||
@@ -25,7 +25,11 @@ class FunctionExporter(Exporter):
|
||||
|
||||
def CheckPolicy(self, func):
|
||||
'Warns the user if this function needs a policy'
|
||||
def IsString(type):
|
||||
return type.const and type.name == 'char' and isinstance(type, PointerType)
|
||||
needs_policy = isinstance(func.result, (ReferenceType, PointerType))
|
||||
if IsString(func.result):
|
||||
needs_policy = False
|
||||
if needs_policy and self.info.policy is None:
|
||||
print '---> Error: Function "%s" needs a policy.' % func.FullName()
|
||||
print
|
||||
|
||||
Reference in New Issue
Block a user