[Interest] My first Shiboken6-based C++ lib bindings

Filippo Rusconi listes.rusconi at laposte.net
Mon Nov 21 17:59:40 CET 2022


Greetings, 

in my desperate :-) quest for a flexible solution to provide scripting to my
Qt6-based C++ project, I am now contemplating Python also. I had tried months
ago to build PySide on my Debian box, but never succeeded.

I tried now again (see [ Procedure ] below) and I can actually build the
shiboken6 generator:

% lls pyside6/pyside-setup/build/testenv/build/shiboken6/generator/shiboken6

-rwxr-xr-x 1 rusconi rusconi 2856416 Nov 17 16:08 pyside6/pyside-setup/build/testenv/build/shiboken6/generator/shiboken6

That was a wonderful start!

And then, I couldn't believe it :-)

--- Build completed (1167s)

(Note that the build fails with CMake but is sucessful with SetupTools -- as
described in an issue that I posted some days ago)

I could run the tetrix.py program (although the doc is erroneous on the location
of the file).

So, now that I actually could build PySide6, comes the actual work.

1. I could build and use the scriptableapplication example.

2. I "transferred" the samplebinding example to my use case, that I describe
below:

The library (Qt6-based) for which I want to develop bindings is named
libpappsomspp and is entirely contained in the "pappso" namespace.

I decided to start with the most simple component: the pappso::DataPoint struct.
The datapoint.h file contains omitted QDataStream stuff, which requires Qt6Core.

namespace pappso
{
struct PMSPP_LIB_DECL DataPoint
{
   pappso_double x = -1;
   pappso_double y = 0;

   DataPoint();
   DataPoint(const DataPoint &other);
   DataPoint(pappso_double x, pappso_double y);
   DataPoint(std::pair<pappso_double, pappso_double> pair);
   DataPoint(const QString &text);

   // For debugging purposes.
   //~DataPoint();

   DataPointCstSPtr makeDataPointCstSPtr() const;

   void initialize(pappso_double x, pappso_double y);
   void initialize(const DataPoint &other);
   bool initialize(const QString &text);

   void reset();

   void incrementX(pappso_double value);
   void incrementY(pappso_double value);

   bool operator==(const DataPoint &other) const;

   DataPoint &operator=(const DataPoint &other);

   bool isValid() const;

   QString toString() const;
   QString toString(int decimals) const;
};
} // namespace pappso

that implements just this: (x,y).


The typesystem file (copied and modified from the sample binding example) is:

<?xml version="1.0"?>

<typesystem package="PyPappsomspp">

   <primitive-type name="bool"/>
   <primitive-type name="std::string"/>

   <load-typesystem name="typesystem_core.xml" generate="no"/>

   <namespace-type name="pappso">

     <object-type name="DataPoint">
     </object-type>

   </namespace-type>

</typesystem>

The project builds, but I have to problems, a series of build warnings and the
fact that upon loading of the generated Python module in Python there is a
missing reference.

First the build warnings:
-------------------------

[ 98%] Building CXX object pybind/CMakeFiles/PyPappsomspp.dir/PyPappsomspp/pypappsomspp_module_wrapper.cpp.o
cd /home/rusconi/devel/pappsomspp/build-area/unix/pybind && /usr/lib/ccache/c++ -DPyPappsomspp_EXPORTS -DQT_CORE_LIB -DQT_NO_DEBUG -DQT_NO_DEBUG_OUTPUT -DQT_NO_KEYWORDS -I/home/rusconi/devel/pappsomspp/build-area/unix -I/home/rusconi/devel/pappsomspp/development -I/home/rusconi/devel/pappsomspp/development/src/pappsomspp -I/usr/include/python3.10 -I/home/rusconi/devel/small-pyside6/lib/python3.10/site-packages/shiboken6_generator/include -I/home/rusconi/devel/small-pyside6/lib/python3.10/site-packages/PySide6/include -I/home/rusconi/devel/small-pyside6/lib/python3.10/site-packages/PySide6/include/QtCore -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -O3 -DNDEBUG -fPIC   -Wno-unknown-pragmas -Wall -Wextra -fPIC -fPIC -std=gnu++17 -MD -MT pybind/CMakeFiles/PyPappsomspp.dir/PyPappsomspp/pypappsomspp_module_wrapper.cpp.o -MF CMakeFiles/PyPappsomspp.dir/PyPappsomspp/pypappsomspp_module_wrapper.cpp.o.d -o CMakeFiles/PyPappsomspp.dir/PyPappsomspp/pypappsomspp_module_wrapper.cpp.o -c /home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:22:1: warning: missing initializer for member ‘PyMethodDef::ml_meth’ [-Wmissing-field-initializers]
    22 | };
       | ^
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:22:1: warning: missing initializer for member ‘PyMethodDef::ml_flags’ [-Wmissing-field-initializers]
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:22:1: warning: missing initializer for member ‘PyMethodDef::ml_doc’ [-Wmissing-field-initializers]
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:116:15: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   116 |     {"clear", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::clear), METH_NOARGS, "clear"},
       |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:117:18: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   117 |     {"pop_back", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_back), METH_NOARGS, "pop_back"},
       |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:118:20: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   118 |     {"removeLast", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_back), METH_NOARGS, "removeLast"},
       |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:121:19: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   121 |     {"pop_front", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_front), METH_NOARGS, "pop_front"},
       |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:122:21: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   122 |     {"removeFirst", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_front), METH_O, "removeFirst"},
       |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:124:18: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   124 |     {"capacity", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::capacity), METH_NOARGS, "capacity"},
       |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:125:14: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   125 |     {"data", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::data), METH_NOARGS, "data"},
       |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/rusconi/devel/pappsomspp/build-area/unix/pybind/PyPappsomspp/pypappsomspp_module_wrapper.cpp:126:19: warning: cast between incompatible function types from ‘PyObject* (*)(PyObject*)’ {aka ‘_object* (*)(_object*)’} to ‘PyCFunction’ {aka ‘_object* (*)(_object*, _object*)’} [-Wcast-function-type]
   126 |     {"constData", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::constData), METH_NOARGS, "constData"},
       |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These warnings seem related to this generated code:

pypappsomspp_module_wrapper.cpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

static PyMethodDef QIntList_methods[] = {
     {"push_back", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::push_back), METH_O, "push_back"},
     {"append", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::push_back), METH_O, "append"},
     {"clear", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::clear), METH_NOARGS, "clear"},
     {"pop_back", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_back), METH_NOARGS, "pop_back"},
     {"removeLast", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_back), METH_NOARGS, "removeLast"},
     {"push_front", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::push_front), METH_O, "push_front"},
     {"prepend", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::push_front), METH_O, "prepend"},
     {"pop_front", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_front), METH_NOARGS, "pop_front"},
     {"removeFirst", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::pop_front), METH_O, "removeFirst"},
     {"reserve", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::reserve), METH_O, "reserve"},
     {"capacity", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::capacity), METH_NOARGS, "capacity"},
     {"data", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::data), METH_NOARGS, "data"},
     {"constData", reinterpret_cast<PyCFunction>(ShibokenSequenceContainerPrivate<QList<int >>::constData), METH_NOARGS, "constData"},
     {nullptr, nullptr, 0, nullptr} // Sentinel
};


Given my total newbie'ness in the field, I cannot tell if these warnings are
serious or not (not even what they actually mean).

Now, for the import error:

I chdir to the directory where the module library PyPappsomspp.so is generated
and I start a python interpreter. When I try to import the module, like this:

(ins)>>> import PyPappsomspp

I get the following error:

ImportError: <dir>/PyPappsomspp.so: undefined symbol: _Z35init_PyPappsomspppappsoStaticFieldsv

Demangled, this yields function: init_PyPappsomspppappsoStaticFields()

I suspect that this function name can be deconstructed like so:

init_<PythonModuleName><namespace>StaticFields().

Has anyone an idea of what error I am doing here or any idea how to explore
where that error and the compilation warnings come from? I feel like I am not
very far from heaven here :-)

Thanks for reading!

Sincerely,
Filippo

-- 
⢀⣴⠾⠻⢶⣦⠀  Filippo Rusconi, PhD
⣾⠁⢠⠒⠀⣿⡁   Research scientist at CNRS
⢿⡄⠘⠷⠚⠋⠀   Debian Developer
⠈⠳⣄⠀⠀⠀⠀  http://msxpertsuite.org
           http://www.debian.org




More information about the Interest mailing list