Is it possible to create instance in C++ program of a class defined in Python module?

168 views Asked by At

What I'm trying to do is create a virtual class (ClassTest) in a C++ program, then import it in Python script, create derived class in that script and then import that derived class back in C++.

Here's the code that I came up with so far:

#include <iostream>
#include <boost/python.hpp>

using namespace boost::python;
using namespace std;

class ClassTest {
public:
    ClassTest () {}

    virtual int someFunction () = 0;
};

BOOST_PYTHON_MODULE(ClassTest) {
    class_< ClassTest, boost::noncopyable >("ClassTest", no_init)
        .def("someFunction", &ClassTest::someFunction)
    ;
}

int main() {
    try {

        Py_Initialize();

        initClassTest();

        ClassTest* testObject = **???**

        cout << "Function result = " << testObject.someFunction() << endl;

        Py_Finalize();

    } catch (error_already_set& e) {

        PyErr_PrintEx(0);
        return 1;

    }

    return 0;
}

And there's Python script:

import ClassTest

class classTestChild(ClassTest.ClassTest):
    def someFunction ():
        return 4;
1

There are 1 answers

8
doqtor On

Here is the complete example how this can be done. You need a wrapper class so that virtual functions can be overridden in Python.

ClassTest.h:

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

    virtual int someFunction () = 0;
};

ClassTest.cpp (must be built into .so/.dll):

class ClassTestWrap : public ClassTest, public python::wrapper<ClassTest>
{
public:
    virtual int someFunction() override
    {
        if (python::override f = this->get_override("someFunction"))
            return f();
        return ClassTestWrap::someFunction();
    }
};

BOOST_PYTHON_MODULE(ClassTest)
{
    python::class_< ClassTestWrap, boost::noncopyable >("ClassTest")
    .def("someFunction", python::pure_virtual(&ClassTestWrap::someFunction));
}

main.cpp (executable):

try
{
    Py_Initialize();
    python::object main = python::import("__main__");
    python::object global(main.attr("__dict__"));
    python::object ignored = python::exec("import ClassTest\n"
                                          "class classTestChild(ClassTest.ClassTest):\n"
                                          "    def someFunction(self):\n"
                                          "        return 4\n"
                                          "\n"
                                          "child = classTestChild()", global);
    ClassTest *obj = python::extract<ClassTest*>(global["child"]);
    int value = obj->someFunction();
    printf("value = %d\n", value);
    Py_Finalize();
}
catch (python::error_already_set&)
{
    PyErr_Print();
    PyErr_Clear();
}

And the output:

value = 4

This example will work if ClassTest.so/ClassTest.dll and .exe are in the same folder and .exe is being called from the same folder (./test.exe)