В первой части статьи мы рассмотрели основы работы с утилитой SIP, предназначенной для создания Python-обвязок (Python bindings) для библиотек, написанных на языках C и C++. Мы рассмотрели основные файлы, которые нужно создать для работы с SIP и начали рассматривать директивы и аннотации. До сих пор мы делали обвязку для простой библиотеки, написанной на языке C. В этой части мы разберемся, как делать обвязку для библиотеки на языке C++, которая содержит классы. На примере этой библиотеки мы посмотрим, какие приемы могут быть полезны при работе с объектно-ориентированной библиотекой, а заодно разберемся с новыми для нас директивами и аннотациями.
Все примеры для статьи доступны в репозитории на github по ссылке: https://github.com/Jenyay/sip-examples.
#ifndef FOO_LIB
#define FOO_LIB
class Foo {
private:
int _int_val;
char* _string_val;
public:
Foo(int int_val, const char* string_val);
virtual ~Foo();
void set_int_val(int val);
int get_int_val();
void set_string_val(const char* val);
char* get_string_val();
};
#endif
#include <string.h>
#include "foo.h"
Foo::Foo(int int_val, const char* string_val): _int_val(int_val) {
_string_val = nullptr;
set_string_val(string_val);
}
Foo::~Foo(){
delete[] _string_val;
_string_val = nullptr;
}
void Foo::set_int_val(int val) {
_int_val = val;
}
int Foo::get_int_val() {
return _int_val;
}
void Foo::set_string_val(const char* val) {
if (_string_val != nullptr) {
delete[] _string_val;
}
auto count = strlen(val) + 1;
_string_val = new char[count];
strcpy(_string_val, val);
}
char* Foo::get_string_val() {
return _string_val;
}
#include <iostream>
#include "foo.h"
using std::cout;
using std::endl;
int main(int argc, char* argv[]) {
auto foo = Foo(10, "Hello");
cout << "int_val: " << foo.get_int_val() << endl;
cout << "string_val: " << foo.get_string_val() << endl;
foo.set_int_val(0);
foo.set_string_val("Hello world!");
cout << "int_val: " << foo.get_int_val() << endl;
cout << "string_val: " << foo.get_string_val() << endl;
}
CC=g++
CFLAGS=-c -fPIC
DIR_OUT=bin
all: main
main: main.o libfoo.a
$(CC) $(DIR_OUT)/main.o -L$(DIR_OUT) -lfoo -o $(DIR_OUT)/main
main.o: makedir main.cpp
$(CC) $(CFLAGS) main.cpp -o $(DIR_OUT)/main.o
libfoo.a: makedir foo.cpp
$(CC) $(CFLAGS) foo.cpp -o $(DIR_OUT)/foo.o
ar rcs $(DIR_OUT)/libfoo.a $(DIR_OUT)/foo.o
makedir:
mkdir -p $(DIR_OUT)
clean:
rm -rf $(DIR_OUT)/*
[build-system]
requires = ["sip >=5, <6"]
build-backend = "sipbuild.api"
[tool.sip.metadata]
name = "pyfoocpp"
version = "0.1"
license = "MIT"
[tool.sip.bindings.pyfoocpp]
headers = ["foo.h"]
libraries = ["foo"]
import os
import subprocess
from sipbuild import Project
class FooProject(Project):
def _build_foo(self):
cwd = os.path.abspath('foo')
subprocess.run(['make'], cwd=cwd, capture_output=True, check=True)
def build(self):
self._build_foo()
super().build()
def build_sdist(self, sdist_directory):
self._build_foo()
return super().build_sdist(sdist_directory)
def build_wheel(self, wheel_directory):
self._build_foo()
return super().build_wheel(wheel_directory)
def install(self):
self._build_foo()
super().install()
%Module(name=foocpp, language="C++")
%DefaultEncoding "UTF-8"
class Foo {
%TypeHeaderCode
#include <foo.h>
%End
public:
Foo(int, const char*);
void set_int_val(int);
int get_int_val();
void set_string_val(const char*);
char* get_string_val();
};
build L-- foocpp +-- apiversions.c +-- array.c +-- array.h +-- bool.cpp +-- build ¦ L-- temp.linux-x86_64-3.8 ¦ +-- apiversions.o ¦ +-- array.o ¦ +-- bool.o ¦ +-- descriptors.o ¦ +-- int_convertors.o ¦ +-- objmap.o ¦ +-- qtlib.o ¦ +-- sipfoocppcmodule.o ¦ +-- sipfoocppFoo.o ¦ +-- siplib.o ¦ +-- threads.o ¦ L-- voidptr.o +-- descriptors.c +-- foocpp.cpython-38-x86_64-linux-gnu.so +-- int_convertors.c +-- objmap.c +-- qtlib.c +-- sipAPIfoocpp.h +-- sipfoocppcmodule.cpp +-- sipfoocppFoo.cpp +-- sip.h +-- sipint.h +-- siplib.c +-- threads.c L-- voidptr.c
/*
* Interface wrapper code.
*
* Generated by SIP 5.1.1
*/
#include "sipAPIfoocpp.h"
#line 6 "/home/jenyay/temp/2/pyfoocpp.sip"
#include <foo.h>
#line 12 "/home/jenyay/temp/2/build/foocpp/sipfoocppFoo.cpp"
PyDoc_STRVAR(doc_Foo_set_int_val, "set_int_val(self, int)");
extern "C" {static PyObject *meth_Foo_set_int_val(PyObject *, PyObject *);}
static PyObject *meth_Foo_set_int_val(PyObject *sipSelf, PyObject *sipArgs)
{
PyObject *sipParseErr = SIP_NULLPTR;
{
int a0;
::Foo *sipCpp;
if (sipParseArgs(&sipParseErr, sipArgs, "Bi", &sipSelf, sipType_Foo, &sipCpp, &a0))
{
sipCpp->set_int_val(a0);
Py_INCREF(Py_None);
return Py_None;
}
}
/* Raise an exception if the arguments couldn't be parsed. */
sipNoMethod(sipParseErr, sipName_Foo, sipName_set_int_val, doc_Foo_set_int_val);
return SIP_NULLPTR;
}
PyDoc_STRVAR(doc_Foo_get_int_val, "get_int_val(self) -> int");
extern "C" {static PyObject *meth_Foo_get_int_val(PyObject *, PyObject *);}
static PyObject *meth_Foo_get_int_val(PyObject *sipSelf, PyObject *sipArgs)
{
PyObject *sipParseErr = SIP_NULLPTR;
{
::Foo *sipCpp;
if (sipParseArgs(&sipParseErr, sipArgs, "B", &sipSelf, sipType_Foo, &sipCpp))
{
int sipRes;
sipRes = sipCpp->get_int_val();
return PyLong_FromLong(sipRes);
}
}
/* Raise an exception if the arguments couldn't be parsed. */
sipNoMethod(sipParseErr, sipName_Foo, sipName_get_int_val, doc_Foo_get_int_val);
return SIP_NULLPTR;
}
PyDoc_STRVAR(doc_Foo_set_string_val, "set_string_val(self, str)");
extern "C" {static PyObject *meth_Foo_set_string_val(PyObject *, PyObject *);}
static PyObject *meth_Foo_set_string_val(PyObject *sipSelf, PyObject *sipArgs)
{
PyObject *sipParseErr = SIP_NULLPTR;
{
const char* a0;
PyObject *a0Keep;
::Foo *sipCpp;
if (sipParseArgs(&sipParseErr, sipArgs, "BA8", &sipSelf, sipType_Foo, &sipCpp, &a0Keep, &a0))
{
sipCpp->set_string_val(a0);
Py_DECREF(a0Keep);
Py_INCREF(Py_None);
return Py_None;
}
}
/* Raise an exception if the arguments couldn't be parsed. */
sipNoMethod(sipParseErr, sipName_Foo, sipName_set_string_val, doc_Foo_set_string_val);
return SIP_NULLPTR;
}
PyDoc_STRVAR(doc_Foo_get_string_val, "get_string_val(self) -> str");
extern "C" {static PyObject *meth_Foo_get_string_val(PyObject *, PyObject *);}
static PyObject *meth_Foo_get_string_val(PyObject *sipSelf, PyObject *sipArgs)
{
PyObject *sipParseErr = SIP_NULLPTR;
{
::Foo *sipCpp;
if (sipParseArgs(&sipParseErr, sipArgs, "B", &sipSelf, sipType_Foo, &sipCpp))
{
char*sipRes;
sipRes = sipCpp->get_string_val();
if (sipRes == SIP_NULLPTR)
{
Py_INCREF(Py_None);
return Py_None;
}
return PyUnicode_FromString(sipRes);
}
}
/* Raise an exception if the arguments couldn't be parsed. */
sipNoMethod(sipParseErr, sipName_Foo, sipName_get_string_val, doc_Foo_get_string_val);
return SIP_NULLPTR;
}
/* Call the instance's destructor. */
extern "C" {static void release_Foo(void *, int);}
static void release_Foo(void *sipCppV, int)
{
delete reinterpret_cast< ::Foo *>(sipCppV);
}
extern "C" {static void dealloc_Foo(sipSimpleWrapper *);}
static void dealloc_Foo(sipSimpleWrapper *sipSelf)
{
if (sipIsOwnedByPython(sipSelf))
{
release_Foo(sipGetAddress(sipSelf), 0);
}
}
extern "C" {static void *init_type_Foo(sipSimpleWrapper *, PyObject *,
PyObject *, PyObject **, PyObject **, PyObject **);}
static void *init_type_Foo(sipSimpleWrapper *, PyObject *sipArgs, PyObject *sipKwds,
PyObject **sipUnused, PyObject **, PyObject **sipParseErr)
{
::Foo *sipCpp = SIP_NULLPTR;
{
int a0;
const char* a1;
PyObject *a1Keep;
if (sipParseKwdArgs(sipParseErr, sipArgs, sipKwds, SIP_NULLPTR, sipUnused, "iA8", &a0, &a1Keep, &a1))
{
sipCpp = new ::Foo(a0,a1);
Py_DECREF(a1Keep);
return sipCpp;
}
}
{
const ::Foo* a0;
if (sipParseKwdArgs(sipParseErr, sipArgs, sipKwds, SIP_NULLPTR, sipUnused, "J9", sipType_Foo, &a0))
{
sipCpp = new ::Foo(*a0);
return sipCpp;
}
}
return SIP_NULLPTR;
}
static PyMethodDef methods_Foo[] = {
{sipName_get_int_val, meth_Foo_get_int_val, METH_VARARGS, doc_Foo_get_int_val},
{sipName_get_string_val, meth_Foo_get_string_val, METH_VARARGS, doc_Foo_get_string_val},
{sipName_set_int_val, meth_Foo_set_int_val, METH_VARARGS, doc_Foo_set_int_val},
{sipName_set_string_val, meth_Foo_set_string_val, METH_VARARGS, doc_Foo_set_string_val}
};
PyDoc_STRVAR(doc_Foo, "\1Foo(int, str)\n"
"Foo(Foo)");
sipClassTypeDef sipTypeDef_foocpp_Foo = {
{
-1,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_TYPE_CLASS,
sipNameNr_Foo,
SIP_NULLPTR,
SIP_NULLPTR
},
{
sipNameNr_Foo,
{0, 0, 1},
4, methods_Foo,
0, SIP_NULLPTR,
0, SIP_NULLPTR,
{SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR,
SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR},
},
doc_Foo,
-1,
-1,
SIP_NULLPTR,
SIP_NULLPTR,
init_type_Foo,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
dealloc_Foo,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
release_Foo,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR,
SIP_NULLPTR
};
>>> from foocpp import Foo
>>> x = Foo(10, 'Hello')
>>> x.get_int_val()
10
>>> x.get_string_val()
'Hello'
>>> x.set_int_val(50)
>>> x.set_string_val('Привет')
>>> x.get_int_val()
50
>>> x.get_string_val()
'Привет'
%Module(name=foocpp, language="C++")
%DefaultEncoding "UTF-8"
class Foo {
%TypeHeaderCode
#include <foo.h>
%End
public:
Foo(int, const char*);
void set_int_val(int);
int get_int_val();
%Property(name=int_val, get=get_int_val, set=set_int_val)
void set_string_val(const char*);
char* get_string_val();
%Property(name=string_val, get=get_string_val, set=set_string_val)
};
>>> from foocpp import Foo
>>> x = Foo(10, "Hello")
>>> x.int_val
10
>>> x.string_val
'Hello'
>>> x.int_val = 50
>>> x.string_val = 'Привет'
>>> x.get_int_val()
50
>>> x.get_string_val()
'Привет'
%Module(name=foocpp, language="C++")
%DefaultEncoding "UTF-8"
class Foo {
%Docstring
Class example from C++ library
%End
%TypeHeaderCode
#include <foo.h>
%End
public:
Foo(int, const char*);
void set_int_val(int);
%Docstring(format="deindented", signature="prepended")
Set integer value
%End
int get_int_val();
%Docstring(format="deindented", signature="prepended")
Return integer value
%End
%Property(name=int_val, get=get_int_val, set=set_int_val)
{
%Docstring "deindented"
The property for integer value
%End
};
void set_string_val(const char*);
%Docstring(format="deindented", signature="appended")
Set string value
%End
char* get_string_val();
%Docstring(format="deindented", signature="appended")
Return string value
%End
%Property(name=string_val, get=get_string_val, set=set_string_val)
{
%Docstring "deindented"
The property for string value
%End
};
};
>>> from foocpp import Foo
>>> Foo.__doc__
'Class example from C++ library'
>>> Foo.get_int_val.__doc__
'get_int_val(self) -> int\nReturn integer value'
>>> Foo.set_int_val.__doc__
'set_int_val(self, int)\nSet integer value'
>>> Foo.get_string_val.__doc__
'Return string value\nget_string_val(self) -> str'
>>> Foo.set_string_val.__doc__
'Set string value\nset_string_val(self, str)'
>>> Foo.int_val.__doc__
'The property for integer value'
>>> Foo.string_val.__doc__
'The property for string value'
>>> help(Foo)
Help on class Foo in module foocpp:
class Foo(sip.wrapper)
| Class example from C++ library
|
| Method resolution order:
| Foo
| sip.wrapper
| sip.simplewrapper
| builtins.object
|
| Methods defined here:
|
| get_int_val(...)
| get_int_val(self) -> int
| Return integer value
|
| get_string_val(...)
| Return string value
| get_string_val(self) -> str
|
| set_int_val(...)
| set_int_val(self, int)
| Set integer value
|
| set_string_val(...)
| Set string value
| set_string_val(self, str)
...
%Module(name=foocpp, language="C++")
%DefaultEncoding "UTF-8"
%DefaultDocstringFormat "deindented"
%DefaultDocstringSignature "prepended"
class Foo {
%Docstring
Class example from C++ library
%End
%TypeHeaderCode
#include <foo.h>
%End
public:
Foo(int, const char*);
void set_int_val(int);
%Docstring
Set integer value
%End
int get_int_val();
%Docstring
Return integer value
%End
%Property(name=int_val, get=get_int_val, set=set_int_val)
{
%Docstring
The property for integer value
%End
};
void set_string_val(const char*);
%Docstring
Set string value
%End
char* get_string_val();
%Docstring
Return string value
%End
%Property(name=string_val, get=get_string_val, set=set_string_val)
{
%Docstring
The property for string value
%End
};
};
>>> from foocpp import Foo
>>> help(Foo)
class Foo(sip.wrapper)
| Class example from C++ library
|
| Method resolution order:
| Foo
| sip.wrapper
| sip.simplewrapper
| builtins.object
|
| Methods defined here:
|
| get_int_val(...)
| get_int_val(self) -> int
| Return integer value
|
| get_string_val(...)
| get_string_val(self) -> str
| Return string value
|
| set_int_val(...)
| set_int_val(self, int)
| Set integer value
|
| set_string_val(...)
| set_string_val(self, str)
| Set string value
...
%Module(name=foocpp, language="C++")
%DefaultEncoding "UTF-8"
%DefaultDocstringFormat "deindented"
%DefaultDocstringSignature "prepended"
class Foo /PyName=Bar/ {
%Docstring
Class example from C++ library
%End
%TypeHeaderCode
#include <foo.h>
%End
public:
Foo(int, const char*);
void set_int_val(int) /PyName=set_integer_value/;
%Docstring
Set integer value
%End
int get_int_val() /PyName=get_integer_value/;
%Docstring
Return integer value
%End
%Property(name=int_val, get=get_integer_value, set=set_integer_value)
{
%Docstring
The property for integer value
%End
};
void set_string_val(const char*) /PyName=set_string_value/;
%Docstring
Set string value
%End
char* get_string_val() /PyName=get_string_value/;
%Docstring
Return string value
%End
%Property(name=string_val, get=get_string_value, set=set_string_value)
{
%Docstring
The property for string value
%End
};
};
>>> from foocpp import Bar
>>> help(Bar)
Help on class Bar in module foocpp:
class Bar(sip.wrapper)
| Class example from C++ library
|
| Method resolution order:
| Bar
| sip.wrapper
| sip.simplewrapper
| builtins.object
|
| Methods defined here:
|
| get_integer_value(...)
| get_integer_value(self) -> int
| Return integer value
|
| get_string_value(...)
| get_string_value(self) -> str
| Return string value
|
| set_integer_value(...)
| set_integer_value(self, int)
| Set integer value
|
| set_string_value(...)
| set_string_value(self, str)
| Set string value
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __weakref__
| list of weak references to the object (if defined)
|
| int_val
| The property for integer value
|
| string_val
| The property for string value
|
| ----------------------------------------------------------------------
...
%Module PyQt5.QtCore
{
%AutoPyName(remove_leading="Q")
}
#ifndef FOO_LIB
#define FOO_LIB
#include <string>
using std::wstring;
class Foo {
private:
int _int_val;
wstring _string_val;
public:
Foo(int int_val, wstring string_val);
void set_int_val(int val);
int get_int_val();
void set_string_val(wstring val);
wstring get_string_val();
};
#endif
#include <string>
#include "foo.h"
using std::wstring;
Foo::Foo(int int_val, wstring string_val):
_int_val(int_val), _string_val(string_val) {}
void Foo::set_int_val(int val) {
_int_val = val;
}
int Foo::get_int_val() {
return _int_val;
}
void Foo::set_string_val(wstring val) {
_string_val = val;
}
wstring Foo::get_string_val() {
return _string_val;
}
#include <iostream>
#include "foo.h"
using std::cout;
using std::endl;
int main(int argc, char* argv[]) {
auto foo = Foo(10, L"Hello");
cout << L"int_val: " << foo.get_int_val() << endl;
cout << L"string_val: " << foo.get_string_val().c_str() << endl;
foo.set_int_val(0);
foo.set_string_val(L"Hello world!");
cout << L"int_val: " << foo.get_int_val() << endl;
cout << L"string_val: " << foo.get_string_val().c_str() << endl;
}
%Module(name=foocpp, language="C++")
%DefaultEncoding "UTF-8"
class Foo {
%TypeHeaderCode
#include <foo.h>
%End
public:
Foo(int, std::wstring);
void set_int_val(int);
int get_int_val();
%Property(name=int_val, get=get_int_val, set=set_int_val)
void set_string_val(std::wstring);
std::wstring get_string_val();
%Property(name=string_val, get=get_string_val, set=set_string_val)
};
%MappedType std::wstring
{
%TypeHeaderCode
#include <string>
%End
%ConvertFromTypeCode
// Convert an std::wstring to a Python (Unicode) string
PyObject* newstring;
newstring = PyUnicode_FromWideChar(sipCpp->data(), -1);
return newstring;
%End
%ConvertToTypeCode
// Convert a Python (Unicode) string to an std::wstring
if (sipIsErr == NULL) {
return PyUnicode_Check(sipPy);
}
if (PyUnicode_Check(sipPy)) {
*sipCppPtr = new std::wstring(PyUnicode_AS_UNICODE(sipPy));
return 1;
}
return 0;
%End
};
$ sip-wheel
These bindings will be built: pyfoocpp.
Generating the pyfoocpp bindings...
sip-wheel: std::wstring is undefined
%ConvertFromTypeCode
// Convert an std::wstring to a Python (Unicode) string
PyObject* newstring;
newstring = PyUnicode_FromWideChar(sipCpp->data(), -1);
return newstring;
%End
%ConvertToTypeCode
// Convert a Python (Unicode) string to an std::wstring
if (sipIsErr == NULL) {
return PyUnicode_Check(sipPy);
}
if (PyUnicode_Check(sipPy)) {
*sipCppPtr = new std::wstring(PyUnicode_AS_UNICODE(sipPy));
return 1;
}
return 0;
%End
>>> from foocpp import Foo
>>> x = Foo(10, 'Hello')
>>> x.string_val
'Hello'
>>> x.string_val = 'Привет'
>>> x.string_val
'Привет'
>>> x.get_string_val()
'Привет'
К сожалению, не доступен сервер mySQL