What is the correct way to import a python module wrapped with swig in c++

我怕爱的太早我们不能终老 提交于 2021-01-07 03:28:48

问题


Creating a python module with swig is the easy part. But what if this module has to interact with my C++ application that imports it from its embedded python instance?

For example @Flexo has made a pretty good example on how to expose the python module to the application here: How can I implement a C++ class in Python, to be called by C++?

All I want is that the application and the module are able to interact with each other like sharing variables and calling callbacks and so on so I found this answer a bit too complex for the task. I couldn't really find much else on how to achieve this and the swig documentation doesn't explain this either ( I guess it has more to do with python and not with SWIG in particular that's why it is not mentioned in the SWIG documentation ).

I then found out that I can also just include the *_wrap.cxx file created by SWIG in my C++ application and achieve the same results (see code below)

I just started with SWIG and Python thats why my question now is, is there a better way than mine or an official way to achieve the same result? Have I overlooked something?
Can I import my swig module before initializing the python instance without having to include the Swigtest_wrap.cxx file?


That's how I made it:

1. Files

following three files contain everything needed for my python module. Containing only a class which I later want to use a as base class for a new python class.

Swigtest.h - header for python module

#ifndef SWIGTEST_H
#define SWIGTEST_H

class Callback
{
public:
    Callback(){}
    virtual ~Callback() {}

    // we want to override this function in python
    virtual void Exec() {}
    static void callFunc();
    static void setCallback(Callback* callback);

private:
    static Callback* m_callback;
};

#endif // SWIGTEST_H

Swigtest.cpp - for python module

Callback* Callback::m_callback = nullptr;

void Callback::callFunc()
{
    if(m_callback != nullptr)
    {
        m_callback->Exec();
        return;
    }
    std::cout << "callback not set" << std::endl;
}

void Callback::setCallback(Callback* callback)
{
    m_callback = callback;
}

Swigtest.i - interface file for SWIG

the only thing to note here is the activation of the "director" feature

%module(directors="1") mymodule

// We need to include Swigtest.h in the SWIG generated C++ file
%{
#include <iostream>
#include "Swigtest.h"
%}

// Enable cross-language polymorphism in the SWIG wrapper.
%feature("director") Callback;

// Tell swig to wrap everything in Swigtest.h
%include "Swigtest.h"

Switest.py

this python script creates a new Class derived from our Callback class and sets the callback to this class.

import mymodule

# lets create a new class derived from callback
class MyPyCallbackFromC(mymodule.Callback):
    def Exec(self):
        print("this class was created in python - It worked!")

callback = mymodule.Callback()
mycallback = MyPyCallbackFromC()
# set callback to this new class
callback.setCallback(mycallback)
# now lets call it from our c++ application

main.cpp

#include <Python.h>
#include <iostream>
#include "Swigtest.h"

// we include this file to be able to append the python
// module table with our own swig wrapped module
#include "Swigtest_wrap.cxx"

int main()
{

    // *_wrap.cxx has to be included for PyInit__mymodule;
    // must be added before the Python instance is initialized!
    PyImport_AppendInittab("_mymodule", PyInit__mymodule);
    Py_Initialize();

    // create Callback class
    Callback* callback = new Callback();
    
    // call Exec() function of linked class
    // should return error because no class is set as Callback yet
    callback->callFunc();

    // execute our script which sets a new class as our callback
    FILE* fp;
    const char* filename;
    filename = "Swigtest.py";
    fp = _Py_fopen(filename, "r");
    PyRun_SimpleFile(fp, filename);

    // if we now call the callback from our application
    // the Exec function that was defined in our python script should be executed
    callback->callFunc();

    delete callback;
    Py_Finalize();
}

2. Building

Building the module

#swig
swig -c++ -python Swigtest.i
#compile
g++ -fpic -c Swigtest.cpp Swigtest_wrap.cxx -I/pathTo/python
#build
g++ -Wall -Wextra -shared Swigtest.o Swigtest_wrap.o -o _mymodule.so

Building the application

#compile
g++ -fpic -c Swigtest.cpp
g++ -fpic -c main.cpp -I/pathTo/python
#build
g++ main.o Swigtest.o -o libmyapp.so -I/pathTo/python -lpython3

3. Execution:

starting the application from the terminal

$ ./libmyapp.so

output

callback not set
this class was created in python - It worked!

That's it.

来源:https://stackoverflow.com/questions/62919022/what-is-the-correct-way-to-import-a-python-module-wrapped-with-swig-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!