A SIP specification consists of some C/C++ type and function declarations and some directives. The declarations may contain annotations which provide SIP with additional information that cannot be expressed in C/C++. SIP does not include a full C/C++ parser.
It is important to understand that a SIP specification describes the Python API, i.e. the API available to the Python programmer when they import the generated module. It does not have to accurately represent the underlying C/C++ library. There is nothing wrong with omitting functions that make little sense in a Python context, or adding functions implemented with handwritten code that have no C/C++ equivalent. It is even possible (and sometimes necessary) to specify a different super-class hierarchy for a C++ class. All that matters is that the generated code compiles properly.
In most cases the Python API matches the C/C++ API. In some cases handwritten code (see %MethodCode) is used to map from one to the other without SIP having to know the details itself. However, there are a few cases where SIP generates a thin wrapper around a C++ method or constructor (see Generated Derived Classes) and needs to know the exact C++ signature. To deal with these cases SIP allows two signatures to be specified. For example:
class Klass
{
public:
// The Python signature is a tuple, but the underlying C++ signature
// is a 2 element array.
Klass(SIP_PYTUPLE) [(int *)];
%MethodCode
int iarr[2];
if (PyArg_ParseTuple(a0, "ii", &iarr[0], &iarr[1]))
{
// Note that we use the SIP generated derived class
// constructor.
Py_BEGIN_ALLOW_THREADS
sipCpp = new sipKlass(iarr);
Py_END_ALLOW_THREADS
}
%End
};
The following is a semi-formal description of the syntax of a specification file.
specification ::= {module-statement}
module-statement ::= [module-directive | statement]
module-directive ::= [
%API |
%CModule |
%CompositeModule |
%ConsolidatedModule |
%Copying |
%DefaultEncoding |
%DefaultMetatype |
%DefaultSupertype |
%Doc |
%ExportedDoc |
%ExportedHeaderCode |
%Feature |
%Import |
%Include |
%InitialisationCode |
%License |
%MappedType |
%Module |
%ModuleCode |
%ModuleHeaderCode |
%OptionalInclude |
%Platforms |
%PreInitialisationCode |
%PostInitialisationCode |
%Timeline |
%UnitCode |
mapped-type-template]
statement :: [class-statement | function | variable]
class-statement :: [
%If |
class |
class-template |
enum |
namespace |
opaque-class |
operator |
struct |
typedef |
exception]
class ::= class name [: super-classes] [class-annotations]
{ {class-line} };
super-classes ::= name [, super-classes]
class-line ::= [
class-statement |
%BIGetBufferCode |
%BIGetReadBufferCode |
%BIGetWriteBufferCode |
%BIGetSegCountCode |
%BIGetCharBufferCode |
%BIReleaseBufferCode |
%ConvertToSubClassCode |
%ConvertToTypeCode |
%Docstring |
%GCClearCode |
%GCTraverseCode |
%PickleCode |
%TypeCode |
%TypeHeaderCode |
constructor |
destructor |
method |
static-method |
virtual-method |
special-method |
operator |
virtual-operator |
class-variable |
public: |
public slots: |
public slots: |
protected: |
protected slots: |
protected slots: |
private: |
private slots: |
private slots: |
signals: |
signals:]
constructor ::= [explicit] name ( [argument-list] )
[exceptions] [function-annotations]
[c++-constructor-signature] ; [%Docstring]
[%MethodCode]
c++-constructor-signature ::= [( [argument-list] )]
destructor ::= [virtual] ~ name () [exceptions] [= 0]
[function-annotations] ; [%MethodCode]
[%VirtualCatcherCode]
method ::= [TQ_SIGNAL] [TQ_SLOT] type name (
[argument-list] ) [const] [exceptions] [= 0]
[function-annotations] [c++-signature] ;
[%Docstring] [%MethodCode]
c++-signature ::= [ type ( [argument-list] )]
static-method ::= static function
virtual-method ::= [TQ_SIGNAL] [TQ_SLOT] virtual type name
( [argument-list] ) [const] [exceptions] [= 0]
[function-annotations] [c++-signature] ;
[%MethodCode] [%VirtualCatcherCode]
special-method ::= type special-method-name
( [argument-list] ) [function-annotations] ;
[%MethodCode]
special-method-name ::= [__abs__ | __add__ | __and__ |
__bool__ | __call__ | __cmp__ | __contains__ |
__delitem__ | __div__ | __eq__ | __float__ |
__floordiv__ | __ge__ | __getitem__ | __gt__ |
__hash__ | __iadd__ | __iand__ | __idiv__ |
__ifloordiv__ | __ilshift__ | __imod__ | __imul__ |
__index__ | __int__ | __invert__ | __ior__ |
__irshift__ | __isub__ | __iter__ | __itruediv__ |
__ixor__ | __le__ | __len__ | __long__ |
__lshift__ | __lt__ | __mod__ | __mul__ |
__ne__ | __neg__ | __next__ | __nonzero__ |
__or__ | __pos__ | __repr__ | __rshift__ |
__setitem__ | __str__ | __sub__ | __truediv__ |
__xor__]
operator ::= operator-type
( [argument-list] ) [const] [exceptions]
[function-annotations] ; [%MethodCode]
virtual-operator ::= virtual operator-type
( [argument-list] ) [const] [exceptions] [= 0]
[function-annotations] ; [%MethodCode]
[%VirtualCatcherCode]
operatator-type ::= [ operator-function | operator-cast ]
operator-function ::= type operator operator-name
operator-cast ::= operator type
operator-name ::= [+ | - | * | / | % | & |
| | ^ | << | >> | += | -= | *= |
/= | %= | &= | |= | ^= | <<= | >>= |
~ | () | [] | < | <= | == | != |
> | >>= | =]
class-variable ::= [static] variable
class-template :: = template < type-list > class
mapped-type-template :: = template < type-list >
%MappedType
enum ::= enum [name] [enum-annotations] { {enum-line} };
enum-line ::= [%If | name [enum-annotations] ,
function ::= type name ( [argument-list] ) [exceptions]
[function-annotations] ; [%Docstring]
[%MethodCode]
namespace ::= namespace name { {namespace-line} };
namespace-line ::= [%TypeHeaderCode | statement]
opaque-class ::= class scoped-name ;
struct ::= struct name { {class-line} };
typedef ::= typedef [typed-name | function-pointer]
typedef-annotations ;
variable::= typed-name [variable-annotations] ;
[%AccessCode] [%GetCode]
[%SetCode]
exception ::= %Exception exception-name [exception-base]
{ [%TypeHeaderCode] %RaiseCode };
exception-name ::= scoped-name
exception-base ::= ( [exception-name | python-exception] )
python-exception ::= [SIP_Exception | SIP_StopIteration |
SIP_StandardError | SIP_ArithmeticError |
SIP_LookupError | SIP_AssertionError |
SIP_AttributeError | SIP_EOFError |
SIP_FloatingPointError | SIP_EnvironmentError |
SIP_IOError | SIP_OSError | SIP_ImportError |
SIP_IndexError | SIP_KeyError | SIP_KeyboardInterrupt |
SIP_MemoryError | SIP_NameError | SIP_OverflowError |
SIP_RuntimeError | SIP_NotImplementedError |
SIP_SyntaxError | SIP_IndentationError | SIP_TabError |
SIP_ReferenceError | SIP_SystemError | SIP_SystemExit |
SIP_TypeError | SIP_UnboundLocalError |
SIP_UnicodeError | SIP_UnicodeEncodeError |
SIP_UnicodeDecodeError | SIP_UnicodeTranslateError |
SIP_ValueError | SIP_ZeroDivisionError |
SIP_WindowsError | SIP_VMSError]
exceptions ::= throw ( [exception-list] )
exception-list ::= scoped-name [, exception-list]
argument-list ::= argument [, argument-list] [, ...]
argument ::= [
type [name] [argument-annotations] [default-value] |
SIP_ANYSLOT [default-value] |
SIP_TQOBJECT |
SIP_RXOBJ_CON |
SIP_RXOBJ_DIS |
SIP_SIGNAL [default-value] |
SIP_SLOT [default-value] |
SIP_SLOT_CON |
SIP_SLOT_DIS]
default-value ::= = expression
expression ::= [value | value binary-operator expression]
value ::= [unary-operator] simple-value
simple-value ::= [scoped-name | function-call | real-value |
integer-value | boolean-value | string-value |
character-value]
typed-name::= type name
function-pointer::= type (* name )( [type-list] )
type-list ::= type [, type-list]
function-call ::= scoped-name ( [value-list] )
value-list ::= value [, value-list]
real-value ::= a floating point number
integer-value ::= a number
boolean-value ::= [true | false]
string-value ::= “ {character} “
character-value ::= ‘ character ‘
unary-operator ::= [! | ~ | - | +]
binary-operator ::= [- | + | * | / | & | |]
argument-annotations ::= see Argument Annotations
class-annotations ::= see Class Annotations
enum-annotations ::= see Enum Annotations
function-annotations ::= see Function Annotations
typedef-annotations ::= see Typedef Annotations
variable-annotations ::= see Variable Annotations
type ::= [const] base-type {*} [&]
type-list ::= type [, type-list]
base-type ::= [scoped-name | template | struct scoped-name |
char | signed char | unsigned char | wchar_t |
int | unsigned | unsigned int |
short | unsigned short |
long | unsigned long |
long long | unsigned long long |
float | double |
bool |
void |
SIP_PYCALLABLE |
SIP_PYDICT |
SIP_PYLIST |
SIP_PYOBJECT |
SIP_PYSLICE |
SIP_PYTUPLE |
SIP_PYTYPE]
scoped-name ::= name [:: scoped-name]
template ::= scoped-name < type-list >
dotted-name ::= name [. dotted-name]
name ::= _A-Za-z {_A-Za-z0-9}
Here is a short list of differences between C++ and the subset supported by SIP that might trip you up.
- SIP does not support the use of [] in types. Use pointers instead.
- A global operator can only be defined if its first argument is a class or a named enum that has been wrapped in the same module.
- Variables declared outside of a class are effectively read-only.
- A class’s list of super-classes doesn’t not include any access specifier (e.g. public).
SIP supports the use of ... as the last part of a function signature. Any remaining arguments are collected as a Python tuple.
SIP supports a number of additional data types that can be used in Python signatures.
This is both a const char * and a PyObject * that is used as the type of the member instead of const char * in functions that implement the connection or disconnection of an explicitly generated signal to a slot. Handwritten code must be provided to interpret the conversion correctly.
This is a PyObject * that is a Python callable object.
This is a PyObject * that is a Python dictionary object.
This is a PyObject * that is a Python list object.
This is a PyObject * of any Python type.
This is a PyObject * that is a Python slice object.
This is a PyObject * that is a Python tuple object.
This is a PyObject * that is a Python type object.
This is a TQObject * that is a C++ instance of a class derived from TQt’s TQObject class.
This is a TQObject * that is a C++ instance of a class derived from TQt’s TQObject class. It is used as the type of the receiver instead of const TQObject * in functions that implement a connection to a slot.
This is a TQObject * that is a C++ instance of a class derived from TQt’s TQObject class. It is used as the type of the receiver instead of const TQObject * in functions that implement a disconnection from a slot.
This is a const char * that is used as the type of the signal instead of const char * in functions that implement the connection or disconnection of an explicitly generated signal to a slot.
This is a const char * that is used as the type of the member instead of const char * in functions that implement the connection or disconnection of an explicitly generated signal to a slot.
This is a const char * that is used as the type of the member instead of const char * in functions that implement the connection of an internally generated signal to a slot. The type includes a comma separated list of types that is the C++ signature of of the signal.
To take an example, TQAccel::connectItem() connects an internally generated signal to a slot. The signal is emitted when the keyboard accelerator is activated and it has a single integer argument that is the ID of the accelerator. The C++ signature is:
bool connectItem(int id, const TQObject *receiver, const char *member);
The corresponding SIP specification is:
bool connectItem(int, SIP_RXOBJ_CON, SIP_SLOT_CON(int));
This is a const char * that is used as the type of the member instead of const char * in functions that implement the disconnection of an internally generated signal to a slot. The type includes a comma separated list of types that is the C++ signature of of the signal.
SIP supports the __div__ and __truediv__ special methods (and the corresponding inplace versions) for both Python v2 and v3.
For Python v2 the __div__ method will be used for both classic and true division if a __truediv__ method is not defined.
For Python v3 the __div__ method will be used for true division if a __truediv__ method is not defined.
For all versions of Python, if both methods are defined then __div__ should be defined first.