* Replacing method code with .setCppCode is now not SIP-specific. The generated code creates a new function to place the code in, like CppMethodDef does, so the given code can use 'self' and return values instead of using special SIP variables.

* added setCppCode_sip for when SIP-specific stuff is needed.
* Adding the 'const' on to const methods had somehow been forgotten, fix that.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@69187 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2011-09-25 04:06:16 +00:00
parent 747eb463b6
commit e44dd28fd4
7 changed files with 169 additions and 89 deletions

View File

@@ -169,16 +169,6 @@ Deprecated C++ items
custom wrapper code
-------------------
Currently function.setCppCode requires some sip-isms (using sipRes and
sipCpp for example) to be able to replace the wrapper code used to
wrap the C++ function. Try making it work more like addCppMethod
where it will generate a new function that is passed "self" and that
you can simply return a value from.
WingIDE support
---------------
WingIDE (and maybe other IDEs too?) has the ability to display

View File

@@ -56,11 +56,11 @@ def run():
gud = c.find('GetUserData')
gud.type = 'wxPyUserData*'
gud.setCppCode('sipRes = dynamic_cast<wxPyUserData*>(sipCpp->GetUserData());')
gud.setCppCode('return dynamic_cast<wxPyUserData*>(self->GetUserData());')
sud = c.find('SetUserData')
sud.find('userData').transfer = True
sud.find('userData').type = 'wxPyUserData*'
sud.setCppCode('sipCpp->SetUserData(dynamic_cast<wxObject*>(userData));')
sud.setCppCode('self->SetUserData(dynamic_cast<wxObject*>(userData));')
c.addPrivateCopyCtor()

View File

@@ -39,14 +39,14 @@ def run():
c.find('SetInstallPrefix').setCppCode("""\
#ifdef __WXMSW__
#else
sipCpp->SetInstallPrefix(*prefix);
self->SetInstallPrefix(*prefix);
#endif
""")
c.find('GetInstallPrefix').setCppCode("""\
#ifdef __WXMSW__
sipRes = new wxString;
return new wxString;
#else
sipRes = new wxString(sipCpp->GetInstallPrefix());
return new wxString(self->GetInstallPrefix());
#endif
""")

View File

@@ -82,7 +82,7 @@ def run():
m.find('externalLeading').out = True
c.find('GetHandle').type = 'void*'
c.find('GetHandle').setCppCode("sipRes = wxPyGetWinHandle(sipCpp);")
c.find('GetHandle').setCppCode("return wxPyGetWinHandle(self);")
c.addCppMethod('void*', 'GetGtkWidget', '()', """\
#ifdef __WXGTK__
@@ -119,16 +119,16 @@ def run():
# and empty stubs otherwise
c.find('RegisterHotKey').setCppCode("""\
#if wxUSE_HOTKEY
sipRes = sipCpp->RegisterHotKey(hotkeyId, modifiers, virtualKeyCode);
return self->RegisterHotKey(hotkeyId, modifiers, virtualKeyCode);
#else
sipRes = false;
return false;
#endif
""")
c.find('UnregisterHotKey').setCppCode("""\
#if wxUSE_HOTKEY
sipRes = sipCpp->UnregisterHotKey(hotkeyId);
return self->UnregisterHotKey(hotkeyId);
#else
sipRes = false;
return false;
#endif
""")
c.find('RegisterHotKey').isVirtual = False
@@ -137,7 +137,7 @@ def run():
c.find('SetDoubleBuffered').setCppCode("""\
#if defined(__WXGTK20__) || defined(__WXMSW__)
sipCpp->SetDoubleBuffered(on);
self->SetDoubleBuffered(on);
#endif
""")

View File

@@ -255,6 +255,21 @@ class FunctionDef(BaseDef):
def releaseGIL(self, release=True):
self.pyReleaseGIL = release
def setCppCode_sip(self, code):
"""
Use the given C++ code instead of that automatically generated by the
back-end. This is similar to adding a new C++ method, except it uses
info we've alread received from the source XML such as the argument
types and names, docstring, etc.
The code generated for this verison will expect the given code to use
SIP specfic variable names, etc. For example::
sipRes = sipCpp->Foo();
"""
self.cppCode = (code, 'sip')
def setCppCode(self, code):
@@ -263,9 +278,15 @@ class FunctionDef(BaseDef):
back-end. This is similar to adding a new C++ method, except it uses
info we've alread received from the source XML such as the argument
types and names, docstring, etc.
"""
self.cppCode = code
The code generated for this version will put the given code in a
wrapper function that will enable it to be more independent, not SIP
specific, and also more natural. For example::
return self->Foo();
"""
self.cppCode = (code, 'function')
def checkForOverload(self, methods):
for m in methods:
@@ -301,6 +322,9 @@ class FunctionDef(BaseDef):
"""
Create a pythonized version of the argsString in function and method
items that can be used as part of the docstring.
TODO: Maybe (optionally) use this syntax to document arg types?
http://www.python.org/dev/peps/pep-3107/
"""
def _cleanName(name):
for txt in ['const', '*', '&', ' ']:
@@ -366,6 +390,7 @@ class MethodDef(FunctionDef):
self.className = className
self.isVirtual = False
self.isStatic = False
self.isConst = False
self.isCtor = False
self.isDtor = False
self.protection = ''
@@ -380,6 +405,7 @@ class MethodDef(FunctionDef):
self.isStatic = element.get('static') == 'yes'
self.isVirtual = element.get('virt') in ['virtual', 'pure-virtual']
self.isPureVirtual = element.get('virt') == 'pure-virtual'
self.isConst = element.get('const') == 'yes'
self.isCtor = self.name == self.className
self.isDtor = self.name == '~' + self.className
self.protection = element.get('prot')
@@ -816,6 +842,19 @@ class CppMethodDef(MethodDef):
self.isConst = isConst
self.__dict__.update(kw)
@staticmethod
def FromMethod(method):
"""
Create a new CppMethodDef that is essentially a copy of a MethodDef,
so it can be used to write the code for a new wrapper function.
TODO: It might be better to just refactor the code in the generator
so it can be shared more easily intstead of using a hack like this...
"""
m = CppMethodDef('', '', '', '')
m.__dict__.update(method.__dict__)
return m
class CppMethodDef_sip(CppMethodDef):
"""

View File

@@ -21,6 +21,12 @@ from cStringIO import StringIO
divider = '//' + '-'*75 + '\n'
phoenixRoot = os.path.abspath(os.path.split(__file__)[0]+'/..')
# This is a list of types that are used as return by value or by reference
# function return types that we need to ensure are actually using pointer
# types in their CppMethodDef or cppCode wrappers.
forcePtrTypes = [ 'wxString',
]
#---------------------------------------------------------------------------
class SipWrapperGenerator(generators.WrapperGeneratorBase):
@@ -176,9 +182,13 @@ from %s import *
_needDocstring = False
if function.cppCode:
stream.write('%MethodCode\n')
stream.write(nci(function.cppCode, 4))
stream.write('%End\n')
code, codeType = function.cppCode
if codeType == 'sip':
stream.write('%MethodCode\n')
stream.write(nci(code, 4))
stream.write('%End\n')
elif codeType == 'function':
raise NotImplementedError() # TODO: See generateMethod for an example, refactor to share code...
for f in function.overloads:
self.generateFunction(f, stream, _needDocstring)
stream.write('\n')
@@ -334,6 +344,7 @@ from %s import *
f(item, stream, indent + ' '*4)
for item in public:
item.klass = klass
f = dispatch[item.__class__]
f(item, stream, indent + ' '*4)
@@ -438,6 +449,8 @@ from %s import *
self.generateParameters(method.items, stream, indent+' '*4)
stream.write(indent)
stream.write(')')
if method.isConst:
stream.write(' const')
if method.isPureVirtual:
stream.write(' = 0')
stream.write('%s;\n' % self.annotate(method))
@@ -449,66 +462,32 @@ from %s import *
_needDocstring = False
if method.cppCode:
stream.write('%s%%MethodCode\n' % indent)
stream.write(nci(method.cppCode, len(indent)+4))
stream.write('%s%%End\n' % indent)
code, codeType = method.cppCode
if codeType == 'sip':
stream.write('%s%%MethodCode\n' % indent)
stream.write(nci(code, len(indent)+4))
stream.write('%s%%End\n' % indent)
elif codeType == 'function':
cm = extractors.CppMethodDef.FromMethod(method)
cm.body = code
self.generateCppMethod(cm, stream, indent, skipDeclaration=True)
stream.write('\n')
if method.overloads:
for m in method.overloads:
self.generateMethod(m, stream, indent, _needDocstring)
def generateCppMethod(self, method, stream, indent=''):
def generateCppMethod(self, method, stream, indent='', skipDeclaration=False):
# Add a new C++ method to a class. This one adds the code as a
# separate function and then adds a call to that function in the
# MethodCode directive.
assert isinstance(method, extractors.CppMethodDef)
if method.ignored:
return
klass = method.klass
if klass:
assert isinstance(klass, extractors.ClassDef)
# create the new function
fargs = method.argsString.strip('()').split(',')
for idx, arg in enumerate(fargs):
# take only the part before the =, if there is one
arg = arg.split('=')[0].strip()
arg = arg.replace('&', '*') # SIP will always want to use pointers for parameters
fargs[idx] = arg
fargs = ', '.join(fargs)
if fargs:
fargs = ', ' + fargs
if method.isCtor:
fname = '_%s_newCtor' % klass.name
fargs = '(int& _isErr%s)' % fargs
stream.write('%s%%TypeCode\n' % indent)
typ = klass.name
if method.useDerivedName:
typ = 'sip'+klass.name
stream.write('%sclass %s;\n' % (indent, typ)) # forward decalre the derived class
stream.write('%s%s* %s%s\n%s{\n' % (indent, typ, fname, fargs, indent))
stream.write(nci(method.body, len(indent)+4))
stream.write('%s}\n' % indent)
stream.write('%s%%End\n' % indent)
else:
if klass:
fname = '_%s_%s' % (klass.name, method.name)
fargs = '(%s* self, int& _isErr%s)' % (klass.name, fargs)
stream.write('%s%%TypeCode\n' % indent)
else:
fname = '_%s_function' % method.name
fargs = '(int& _isErr%s)' % fargs
stream.write('%s%%ModuleCode\n' % indent)
stream.write('%s%s %s%s\n%s{\n' % (indent, method.type, fname, fargs, indent))
stream.write(nci(method.body, len(indent)+4))
stream.write('%s}\n' % indent)
stream.write('%s%%End\n' % indent)
# now insert the method declaration and the code to call the new function
# first, find the parameter names
pnames = method.argsString.strip('()').split(',')
lastP = method.argsString.rfind(')')
pnames = method.argsString[:lastP].strip('()').split(',')
for idx, pn in enumerate(pnames):
# take only the part before the =, if there is one
name = pn.split('=')[0].strip()
@@ -521,19 +500,83 @@ from %s import *
pnames = ', ' + pnames
typ = method.type
argsString = method.argsString
# spit it all out
if not skipDeclaration:
# First insert the method declaration
if method.isCtor:
stream.write('%s%s%s%s;\n' %
(indent, method.name, argsString, self.annotate(method)))
else:
constMod = ""
if method.isConst:
constMod = " const"
stream.write('%s%s %s%s%s%s;\n' %
(indent, typ, method.name, argsString, constMod, self.annotate(method)))
# write the docstring
self.generateDocstring(method, stream, indent)
klass = method.klass
if klass:
assert isinstance(klass, extractors.ClassDef)
# create the new function
fstream = StringIO() # using a new stream so we can do the actual write a little later
lastP = method.argsString.rfind(')')
fargs = method.argsString[:lastP].strip('()').split(',')
for idx, arg in enumerate(fargs):
# take only the part before the =, if there is one
arg = arg.split('=')[0].strip()
arg = arg.replace('&', '*') # SIP will always want to use pointers for parameters
fargs[idx] = arg
fargs = ', '.join(fargs)
if fargs:
fargs = ', ' + fargs
if method.isCtor:
stream.write('%s%s%s%s;\n' %
(indent, method.name, argsString, self.annotate(method)))
else:
constMod = ""
if method.isConst:
constMod = " const"
stream.write('%s%s %s%s%s%s;\n' %
(indent, typ, method.name, argsString, constMod, self.annotate(method)))
self.generateDocstring(method, stream, indent)
fname = '_%s_newCtor' % klass.name
fargs = '(int& _isErr%s)' % fargs
fstream.write('%s%%TypeCode\n' % indent)
typ = klass.name
if method.useDerivedName:
typ = 'sip'+klass.name
fstream.write('%sclass %s;\n' % (indent, typ)) # forward decalre the derived class
fstream.write('%s%s* %s%s\n%s{\n' % (indent, typ, fname, fargs, indent))
fstream.write(nci(method.body, len(indent)+4))
fstream.write('%s}\n' % indent)
fstream.write('%s%%End\n' % indent)
else:
if klass:
fname = '_%s_%s' % (klass.name, method.name)
if method.isStatic:
# If the method is static then there is no sipCpp to send to
# the new function, so it should not have a self parameter.
fargs = '(int& _isErr%s)' % fargs
else:
fargs = '(%s* self, int& _isErr%s)' % (klass.name, fargs)
fstream.write('%s%%TypeCode\n' % indent)
else:
fname = '_%s_function' % method.name
fargs = '(int& _isErr%s)' % fargs
fstream.write('%s%%ModuleCode\n' % indent)
# If the return type is in the forcePtrTypes list then make sure
# that it is a pointer, not a return by value or reference, since
# SIP almost always deals with pointers to newly allocated
# objects.
typPtr = method.type
if typPtr in forcePtrTypes:
if '&' in typPtr:
typPtr.replace('&', '*')
elif '*' not in typPtr:
typPtr += '*'
fstream.write('%s%s %s%s\n%s{\n' % (indent, typPtr, fname, fargs, indent))
fstream.write(nci(method.body, len(indent)+4))
fstream.write('%s}\n' % indent)
fstream.write('%s%%End\n' % indent)
# Write the code that will call the new function
stream.write('%s%%MethodCode\n' % indent)
stream.write(indent+' '*4)
if method.isCtor:
@@ -542,12 +585,20 @@ from %s import *
if method.type != 'void':
stream.write('sipRes = ')
if klass:
stream.write('%s(sipCpp, sipIsErr%s);\n' % (fname, pnames))
if method.isStatic:
# If the method is static then there is no sipCpp to send to
# the new function, so it should not have a self parameter.
stream.write('%s(sipIsErr%s);\n' % (fname, pnames))
else:
stream.write('%s(sipCpp, sipIsErr%s);\n' % (fname, pnames))
else:
stream.write('%s(sipIsErr%s);\n' % (fname, pnames))
stream.write('%s%%End\n\n' % indent)
stream.write('%s%%End\n' % indent)
# and finally, add the new function itself
stream.write(fstream.getvalue())
stream.write('\n')
def generateCppMethod_sip(self, method, stream, indent=''):

View File

@@ -32,7 +32,7 @@ def removeWxPrefixes(node):
extractors.MethodDef )): # TODO: Any others?
item.pyName = item.name[2:]
item.wxDropped = True
if item.name.startswith('wxEVT_'):
if item.name.startswith('wxEVT_') and 'CATEGORY' not in item.name:
# give these their actual name so the auto-renamer won't touch them
item.pyName = item.name