Search code examples
c++pythonswigboost-python

How to expose a C++ class to Python without building a module


I want to know if there is any way to expose a C++ class to Python but without building an intermediate shared library.

Here is my desirable scenario. For example I have following C++ class:

class toto
     {
     public:
        toto(int iValue1_, int iValue2_): iValue1(iValue1_), iValue2(iValue2_) {}
        int Addition(void) const {if (!this) return 0; return iValue1 + iValue2;}

      private:
        int iValue1;
        int iValue2;
     };

I would like to convert somehow this class (or its intance) to a PyObject* in order to send it as paremter (args) to for example PyObject_CallObject:

PyObject* PyObject_CallObject(PyObject* wrapperFunction, PyObject* args)

In the other hand in my python side, I'll have a wrapperFunction which gets the pointer on my C++ class (or its instance) as parameter and it calls its methods or uses its properties:

def wrapper_function(cPlusPlusClass):
    instance = cPlusPlusClass(4, 5)
    result = instance.Addition()

As you can see, I don't really need/want to have a separate shared library or build a module by boost python. All that I need is to find a way to convert a C++ code to PyObject and send it to python. I cannot find a way to do that by C python libraries, boost or SWIG.


Solution

  • I found my answer. Actually what I was searching was pretty similar to this answer (thanks moooeeeep for his comment):

      Exposing a C++ class instance to a python embedded interpreter

    Following C++ class (Attention! default constructor is mandatory):

    class TwoValues
    {
    public:
        TwoValues(void): iValue1(0), iValue2(0) {}
        TwoValues(int iValue1, int iValue2): iValue1(iValue1_), iValue2(iValue2_) {}
    
        int Addition(void) const {if (!this) return 0; return iValue1 + iValue2;}
    
    public:
        int iValue1;
        int iValue2;
    };
    

    could be exposed by boost by following macro:

    BOOST_PYTHON_MODULE(ModuleTestBoost)
    {
    class_<TwoValues>("TwoValues")
       .def("Addition",             &TWOVALUES::Addition)
       .add_property("Value1",      &TWOVALUES::iValue1)
       .add_property("Value2",      &TWOVALUES::iValue2);
    };
    

    In the other hand I have a python function defined in python_script.py which takes an instance of this class and do something. For example:

    def wrapper_function(instance):
        result = instance.Addition()
        myfile = open(r"C:\...\testboostexample.txt", "w")
        output = 'First variable is {0}, second variable is {1} and finally the addition is {2}'.format(instance.Value1, instance.Value2, result)
        myfile .write(output)
        myfile .close()
    

    Then in C++ side, I can call this function by sending at the same time the instance of my class, like this:

    Py_Initialize();
    
    try
        {
        TwoValues instance(5, 10);
        initModuleTestBoost();
    
        object python_script = import("python_script");
        object wrapper_function = python_script.attr("wrapper_function");
        wrapper_function(&instance);
        }
    catch (error_already_set)
        {
        PyErr_Print();
        }
    
    Py_Finalize();
    

    Advantages:

    • I don't need build any shared library or binary
    • As I'm using Boost, I don't need to be worry about memory management & reference counting
    • I don't use shared boost pointer (boost::shared_ptr) to point to the instance of my class