2
0
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:
Bruno da Silva de Oliveira
2003-03-12 01:32:00 +00:00
parent 415991f6fc
commit ca9920874f
2 changed files with 209 additions and 167 deletions

View File

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

View File

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