Handwritten code
Look at the cursorPosition method. It
does something Python can't do: it returns values via pointer
arguments (the two int * parameters) passed
to the method, instead of returning a tuple of values.
Call by reference and call by value: C and C++ (and Visual Basic and a host of other
programming languages) have two ways of passing arguments to
functions: by reference, or by value. If a function is
called with arguments by reference, changing the value of
the arguments will change their value outside the function.
Python only has call by (object) reference. If an object is
mutable, then changing it inside a function, will also
change the object outside a function:
>>> def f(a):
... a.append("bla")
...
>>> a=["bla"]
>>> f(a)
>>> a
['bla', 'bla']
When you can a Python function with an immutable object
like a string or an integer, the value of the reference
outside the function won't change:
>>>
>>> def f(a):
... a="b"
...
>>> a="a"
>>> f(a)
>>> a
'a'
>>>
That's because the name of the argument and the name of
the variable are not aliases, they are two seperate names,
that might point to different objects, or to the same. As
soon as you assign a new string to the argument, the
references no longer point to the same name.
%MemberCode
// The Python interface returns a tuple.
QMultiLineEdit *ptr;
if (sipParseArgs(&sipArgsParsed,sipArgs,
"m",
sipThisObj,
sipClass_QMultiLineEdit,
&ptr))
{
int line, col;
ptr -> QMultiLineEdit::cursorPosition(&line,&col);
return Py_BuildValue("(ii)",line,col);
}
%End
However, sip can't
determine whether these pointers contain data being sent to
the cursorPosition function, or data
being returned by cursorPosition to the
calling code. Since Python has nothing comparable to a C/C++
pointer, there is no automatic way to generate the wrapper for
this method. Inside knowledge of what the code actually does
is required to generate wrappers, and the developer has to
provide sip with this knowledge.
Immediately following the cursorPosition
method declaration is a %MemberCode
declaration. This allows the developer to tell
sip how to wrap this function by providing
most of the necessary C++ code. The contents of the
%Membercode block is, in fact, C++
code.
Looking at the
%Membercode block, sip
is instructed to first parse the arguments which Python will
pass to the wrapper code. The
sipParseArgs function is the most
important, and complex, function in the sip
library. The third parameter is a string that encodes the
number and types of the Python parameters that are expected,
with more information given in later parameters. In this case
the sipParseArgs is being told to expect
exactly one Python parameter which is a Python instance of the
QMultiLineEdit class which corresponds
to the value of self.
Next, some variables are defined
(line, col) to hold the
data cursorPosition will return. The
member next calls the Qt/C++ version of
QMultiLineEdit::cursorPosition,
which will fill in the values pointed to by
&line and
&col.
Last, the code uses the
Py_BuildValue to return the
line and col values
obtained to the Python caller. In this case, since two values
are returned, the code places the two values in a tuple which
is returned to the Python program that called the method. Even
though a Python method can return 2 or more distinct values,
C++ can't, and the member code is in the C++ domain.
If you look at some of the code
sip generates, you'll find it looks very
much like the member code generated manually for the
cursorPosition method. There will be a number of references to
functions or methods which begin with ‘sip' or
‘Py'. The ‘Py'-prefixed calls are to code built
into Python itself. Python contains a library of functions
used to write wrappers/interfaces for C and C++ code. The
‘sip'-prefixed functions are calls to the
sip library, and occur in all
sip-generated code (including all of PyQt).
This is why you need to have the sip
library installed before you can use PyQt, even though you
don't need to run the sip program
itself.
Other limitations
Presently, sip can't
automatically generate code for methods which have parameters
or return values with types like int&,
int* or bool*.
In addition, sip can't
directly handle types based on C++ templates. In these cases
you have to used the %MappedType
directive to supply your own C++ code to explicitly handle how
such types are converted to and from Python objects. You still
use the C++ template type in parameters and return values of
functions and methods, sip will
automatically carry out the necessary conversions based on the
C++ code you provided.