diff --git a/TODO.txt b/TODO.txt index 7672bf44..530b7d88 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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 diff --git a/etg/sizer.py b/etg/sizer.py index 766f1a70..1ace3fc6 100644 --- a/etg/sizer.py +++ b/etg/sizer.py @@ -56,11 +56,11 @@ def run(): gud = c.find('GetUserData') gud.type = 'wxPyUserData*' - gud.setCppCode('sipRes = dynamic_cast(sipCpp->GetUserData());') + gud.setCppCode('return dynamic_cast(self->GetUserData());') sud = c.find('SetUserData') sud.find('userData').transfer = True sud.find('userData').type = 'wxPyUserData*' - sud.setCppCode('sipCpp->SetUserData(dynamic_cast(userData));') + sud.setCppCode('self->SetUserData(dynamic_cast(userData));') c.addPrivateCopyCtor() diff --git a/etg/stdpaths.py b/etg/stdpaths.py index b7d5c4b5..886b3804 100644 --- a/etg/stdpaths.py +++ b/etg/stdpaths.py @@ -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 """) diff --git a/etg/window.py b/etg/window.py index eb40bbcd..f06c1cdd 100644 --- a/etg/window.py +++ b/etg/window.py @@ -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 """) diff --git a/etgtools/extractors.py b/etgtools/extractors.py index 4093ad49..538ecb10 100644 --- a/etgtools/extractors.py +++ b/etgtools/extractors.py @@ -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): """ diff --git a/etgtools/sip_generator.py b/etgtools/sip_generator.py index 5b437449..5bb8d84b 100644 --- a/etgtools/sip_generator.py +++ b/etgtools/sip_generator.py @@ -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=''): diff --git a/etgtools/tweaker_tools.py b/etgtools/tweaker_tools.py index 0ff0d200..1781d714 100644 --- a/etgtools/tweaker_tools.py +++ b/etgtools/tweaker_tools.py @@ -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