5

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.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
Sacha
  • 134
  • 2
  • 12
  • 1
    I'm not sure I understand exactly what you want. Boost.Python has `boost::python::object::ptr`, which returns a `PyObject*` -- is this what you were looking for? – Paul Manta Aug 26 '13 at 11:01
  • 1
    I believe it's possible to access C/C++ via `ctypes`, but it might be a difficult task to expose a C++ class. – Bakuriu Aug 26 '13 at 12:44
  • @PaulManta: Thanks for the answer. What I want consists in 3 steps: Step1: encapsulate a C++ class in PyObject pointer.Step2 send this pointer to a python function. Step3. using this C++ pointer in python (i.e. calling its methods). So could you please be more pragmatic & tell be by an example how can I encapsulate C++ code to object::ptr? – Sacha Aug 26 '13 at 15:22
  • 1
    Do you want to use the Python interpreter from your C++ code? Have you had a look at this question: http://stackoverflow.com/q/8225934/1025391 ? – moooeeeep Aug 26 '13 at 18:08
  • Thank you @moooeeeep! yes you are right, actually this answer is pretty similar to what I'm trying to do. I updated the answer. In addition, we can get rid of boost::shared pointer. – Sacha Aug 28 '13 at 16:25

2 Answers2

7

As far as I know, there is no easy way to accomplish this.

To extend Python with C++ with neither a module nor an intermediate library, it would require dynamically loading a library, then importing the functions. This approach is used by the ctypes module. To accomplish the same with C++, one would need to write a ctypes-like library that understood the C++ ABI for the target compiler(s).

To extend Python without introducing a module, an intermediate library could be created that provided a C API that wraps the C++ library. This intermediate library could then be used in Python through ctypes. While it does not provide the exact calling syntax and does introduce an intermediate library, it would likely be less effort than building a ctypes-like library that could interface directly with C++.

However, if an intermediate library is going to be introduced, it may be worthwhile to use Boost.Python, SWIG, or some other C++/Python language binding tool. While many of these tools will introduce the extension via a module, they often provide cleaner calling conventions, better error checking in the binding process, and may be easier to maintain.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
1

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
Community
  • 1
  • 1
Sacha
  • 134
  • 2
  • 12