how to expose a C++ function to Python

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
User avatar
uwestoehr
Veteran
Posts: 4961
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany
Contact:

how to expose a C++ function to Python

Post by uwestoehr »

I have a simple task:
- I wrote a C++ function and need to call it from Python

I spent a lot of time to make this working but still doesn't understand what my mistake is. here is what i have so far:
https://github.com/FreeCAD/FreeCAD/pull/7300

I assume my mistake is somewhere in the file ViewProviderFemPostPipeline.cpp in the Python connection part at the end.

Could anybody give me a pointer how it is done in general? Maybe then I find my bug.
wmayer
Founder
Posts: 20241
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: how to expose a C++ function to Python

Post by wmayer »

  1. Register FemPostPipelinePython (i.e. Fem::FemPostPipelinePython::init())
  2. Register ViewProviderFemPostPipelinePython after ViewProviderFemPostPipeline
  3. Implement ViewProviderFemPostPipelinePython::getPyObject()
EDIT: Small correction:
Instead of ViewProviderFemPostPipelinePython::getPyObject() you should re-implement ViewProviderFemPostPipeline::getPyObject() because you probably want to access this new method when you create an FemPostPipeline object.

Code: Select all

diff --git a/src/Mod/Fem/App/AppFem.cpp b/src/Mod/Fem/App/AppFem.cpp
index 9d4ed26626..a6aac4a389 100644
--- a/src/Mod/Fem/App/AppFem.cpp
+++ b/src/Mod/Fem/App/AppFem.cpp
@@ -177,6 +177,7 @@ PyMOD_INIT_FUNC(Fem)
 #ifdef FC_USE_VTK
     Fem::FemPostObject                        ::init();
     Fem::FemPostPipeline                      ::init();
+    Fem::FemPostPipelinePython                ::init();
     Fem::FemPostFilter                        ::init();
     Fem::FemPostClipFilter                    ::init();
     Fem::FemPostCutFilter                     ::init();
diff --git a/src/Mod/Fem/Gui/AppFemGui.cpp b/src/Mod/Fem/Gui/AppFemGui.cpp
index ffad1a0876..30ef85f519 100644
--- a/src/Mod/Fem/Gui/AppFemGui.cpp
+++ b/src/Mod/Fem/Gui/AppFemGui.cpp
@@ -136,8 +136,6 @@ PyMOD_INIT_FUNC(FemGui)
     FemGui::ViewProviderFemMeshShapeNetgen                      ::init();
     FemGui::PropertyFemMeshItem                                 ::init();
 
-    FemGui::ViewProviderFemPostPipelinePython                   ::init();
-
     FemGui::ViewProviderSetElements                             ::init();
     FemGui::ViewProviderSetFaces                                ::init();
     FemGui::ViewProviderSetGeometry                             ::init();
@@ -153,6 +151,7 @@ PyMOD_INIT_FUNC(FemGui)
 #ifdef FC_USE_VTK
     FemGui::ViewProviderFemPostObject                           ::init();
     FemGui::ViewProviderFemPostPipeline                         ::init();
+    FemGui::ViewProviderFemPostPipelinePython                   ::init();
     FemGui::ViewProviderFemPostClip                             ::init();
     FemGui::ViewProviderFemPostCut                              ::init();
     FemGui::ViewProviderFemPostDataAlongLine                    ::init();
diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp b/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp
index b8d893ec99..f1b7d7ca58 100644
--- a/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp
+++ b/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp
@@ -26,6 +26,7 @@
 #include <vtkPointData.h>
 #endif
 
+#include <App/FeaturePythonPyImp.h>
 #include <App/GroupExtension.h>
 #include <Base/Console.h>
 #include <Gui/Application.h>
@@ -173,6 +174,14 @@ namespace Gui
 /// @cond DOXERR
 PROPERTY_SOURCE_TEMPLATE(FemGui::ViewProviderFemPostPipelinePython, FemGui::ViewProviderFemPostPipeline)
 /// @endcond
+template<> PyObject* FemGui::ViewProviderFemPostPipelinePython::getPyObject() {
+    if (!pyViewObject) {
+        // ref counter is set to 1
+        pyViewObject = new App::FeaturePythonPyT<FemGui::ViewProviderFemPostPipelinePy>(this);
+    }
+    pyViewObject->IncRef();
+    return pyViewObject;
+}
 
 // explicit template instantiation
 template class FemGuiExport ViewProviderPythonFeatureT<ViewProviderFemPostPipeline>;
Currently the class FemPostPipelinePython causes a crash when you type this into the Python console:

Code: Select all

d = App.newDocument()
d.addObject("Fem::FemPostPipelinePython")
d.ActiveObject. # CRASH!
The bug is in PropertyPostDataObject::getMemSize() because it accesses the null pointer m_dataObject. But the question is why the pointer (apparently) is not null for a FemPostPipeline.

Stacktrace:

Code: Select all

Program received signal SIGSEGV, Segmentation fault.
#0  /lib/x86_64-linux-gnu/libc.so.6(+0x3ef10) [0x7f9dfb36bf10]
#1  0x7f9d825c23d9 in Fem::PropertyPostDataObject::getMemSize() const from /home/user/Projects/build_clang/Mod/Fem/Fem.so+0x19
#2  0x7f9e02dfd1d1 in App::PropertyContainer::getMemSize() const from /home/user/Projects/build_clang/lib/libFreeCADApp.so+0xe1
#3  0x7f9e01f8554c in Base::PersistencePy::getMemSize() const from /home/user/Projects/build_clang/lib/libFreeCADBase.so+0x2c
#4  0x7f9e01f84a9a in Base::PersistencePy::staticCallback_getMemSize(_object*, void*) from /home/user/Projects/build_clang/lib/libFreeCADBase.so+0xba
#5  /usr/lib/x86_64-linux-gnu/libpython3.6m.so.1.0(_PyObject_GenericGetAttrWithDict+0x7e) [0x7f9e011491ae]
#6  0x7f9e01f94be1 in Base::PyObjectBase::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADBase.so+0x131
#7  0x7f9e01efeb7f in Base::BaseClassPy::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADBase.so+0x28f
#8  0x7f9e01f859bf in Base::PersistencePy::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADBase.so+0x28f
#9  0x7f9e02e0cdbf in App::PropertyContainerPy::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADApp.so+0x28f
#10  0x7f9e02c7ab9f in App::ExtensionContainerPy::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADApp.so+0x28f
#11  0x7f9e02ca7b3f in App::DocumentObjectPy::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADApp.so+0x28f
#12  0x7f9e02d533a9 in App::FeaturePythonPyT<App::DocumentObjectPy>::_getattr(char const*) from /home/user/Projects/build_clang/lib/libFreeCADApp.so+0x1a9
#13  0x7f9e01f93db2 in Base::PyObjectBase::__getattro(_object*, _object*) from /home/user/Projects/build_clang/lib/libFreeCADBase.so+0x192
#14  /usr/lib/x86_64-linux-gnu/libpython3.6m.so.1.0(PyObject_GetAttrString+0x34) [0x7f9e01149c34]
#15  0x7f9e042beb2f in Py::Object::getAttr(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const from /home/user/Projects/build_clang/lib/libFreeCADGui.so+0x3f
#16  0x7f9e047d1fa1 in Gui::CallTipsList::extractTipsFromObject(Py::Object&, Py::List&, QMap<QString, Gui::CallTip>&) const from /home/user/Projects/build_clang/lib/libFreeCADGui.so+0x131
#17  0x7f9e047d14d9 in Gui::CallTipsList::extractTips(QString const&) const from /home/user/Projects/build_clang/lib/libFreeCADGui.so+0x10a9
#18  0x7f9e047d3855 in Gui::CallTipsList::showTips(QString const&) from /home/user/Projects/build_clang/lib/libFreeCADGui.so+0x685
#19  0x7f9e047e44bf in Gui::PythonConsole::keyPressEvent(QKeyEvent*) from /home/user/Projects/build_clang/lib/libFreeCADGui.so+0x7ff
User avatar
wandererfan
Veteran
Posts: 6265
Joined: Tue Nov 06, 2012 5:42 pm
Contact:

Re: how to expose a C++ function to Python

Post by wandererfan »

uwestoehr wrote: Wed Aug 03, 2022 2:12 am
ViewProviderFemPostPipelinePy.xml is not right. ViewProviderFemPostPipeline inherits from ViewProviderFemPostObject,
but the xml says ViewProviderFemPostPipelinePy inherits from ViewProviderDocumentObjectPy. It should be:

Code: Select all

            Father="ViewProviderFemPostObjectPy"
            Name="ViewProviderFemPostPipelinePy"
            Twin="ViewProviderFemPostPipeline"
            TwinPointer="ViewProviderFemPostPipeline"
            Include="Mod/Fem/Gui/ViewProviderFemPostPipeline.h"
            Namespace="FemGui"
            FatherInclude="Mod/Fem/Gui/ViewProviderFemPostObject.h"
            FatherNamespace="FemGui"
wmayer
Founder
Posts: 20241
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: how to expose a C++ function to Python

Post by wmayer »

ViewProviderFemPostPipelinePy.xml is not right. ViewProviderFemPostPipeline inherits from ViewProviderFemPostObject,
but the xml says ViewProviderFemPostPipelinePy inherits from ViewProviderDocumentObjectPy. It should be:
The ViewProviderFemPostPipelinePy.xml is fine.

For each ViewProvider**** class we don't have to implement a corresponding ViewProvider***Py class but in most cases the ViewProviderDocumentObjectPy is sufficient. So, that's why it's correct that ViewProviderFemPostPipelinePy inherits from ViewProviderDocumentObjectPy. Besides this we don't even have a class ViewProviderFemPostObjectPy -- because we never needed it.
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: how to expose a C++ function to Python

Post by Kunda1 »

Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
User avatar
uwestoehr
Veteran
Posts: 4961
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany
Contact:

Re: how to expose a C++ function to Python

Post by uwestoehr »

Many thanks for all your help!

However, I did everything as suggested and this was the result:
https://github.com/FreeCAD/FreeCAD/pull ... af1b01706a

Sadly, this did not work. The I realized that the App part also does not have an ::init() of a python class. So I removed it to get App as it is in master and modified the Viewprovider part accordingly.
In effect I applied these changes:
https://github.com/FreeCAD/FreeCAD/pull ... 2ebe5346ee

And now it works!

It seems that i forgot to add to the Viewprovider a

Code: Select all

PyObject *getPyObject();
function.

However, why is the type definition of a

Code: Select all

FemGui::ViewProviderFemPostPipelinePython
not necessary. Following Werner's post, it was correct that I did this.
Post Reply