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.