12

I want to know how to wrap a C++ object with Python Extension API (and distutils) without external tools (like Cython, Boost, SWIG, ...). Just in pure Python way without creating a dll.

Note that my C++ object has memory allocations so destructor has to be called to avoid memory leaks.

#include "Voice.h"

namespace transformation
{ 
  Voice::Voice(int fftSize) { mem=new double[fftSize]; } 

  Voice::~Voice() { delete [] mem; } 

  int Voice::method1() { /*do stuff*/ return (1); } 
}

I just want to do somethings like that in Python :

import voice

v=voice.Voice(512)
result=v.method1()
5
  • You won't be able to do that well unless you read and understand docs.python.org/3.6/c-api/index.html Commented Feb 14, 2018 at 12:03
  • OK. But maybe you have a link to an example. Examples are often more understandable than official docs (even if it is usefull when you want to go deep inside). I haven't found anything. Commented Feb 14, 2018 at 12:12
  • Use python modules source code as examples: github.com/python/cpython/tree/master/Modules Commented Feb 14, 2018 at 12:21
  • 2
    The value provided by Boost.Python, SWIG, etc, is that you do not have to know/understand all the low level details. Commented Feb 14, 2018 at 12:23
  • I can't believe that Python designers have not provided a simple way to do that. Commented Feb 14, 2018 at 13:40

1 Answer 1

20

Seems that the answer was in fact here : https://docs.python.org/3.6/extending/newtypes.html

With examples, but not really easy.

EDIT 1 :

In fact, it is not really for wrapping a C++ object in a Python object, but rather to create a Python object with C code. (edit2 : and so you can wrap C++ object!)

EDIT 2 :

Here is a solution using the Python newtypes

.

Original C++ file : Voice.cpp

#include <cstdio>

#include "Voice.h"

namespace transformation
{ 
    Voice::Voice(int fftSize) {
        printf("c++ constructor of voice\n");
        this->fftSize=fftSize;
        mem=new double[fftSize];
        } 

    Voice::~Voice() { delete [] mem; } 

    int Voice::filter(int freq) {
        printf("c++ voice filter method\n");
        return (doubleIt(3));
    }
    int Voice::doubleIt(int i) { return 2*i; }
}

.

Original h file : Voice.h

namespace transformation {

    class Voice {
    public:
        double *mem;
        int fftSize;

        Voice(int fftSize);
        ~Voice();

        int filter(int freq);
        int doubleIt(int i);
    };

}

.

C++ Python wrapper file : voiceWrapper.cpp

#include <Python.h>

#include <cstdio>
//~ #include "structmember.h"

#include "Voice.h"

using transformation::Voice;

typedef struct {
    PyObject_HEAD
    Voice * ptrObj;
} PyVoice;




static PyModuleDef voicemodule = {
    PyModuleDef_HEAD_INIT,
    "voice",
    "Example module that wrapped a C++ object",
    -1,
    NULL, NULL, NULL, NULL, NULL
};

static int PyVoice_init(PyVoice *self, PyObject *args, PyObject *kwds)
// initialize PyVoice Object
{
    int fftSize;

    if (! PyArg_ParseTuple(args, "i", &fftSize))
        return -1;

    self->ptrObj=new Voice(fftSize);

    return 0;
}

static void PyVoice_dealloc(PyVoice * self)
// destruct the object
{
    delete self->ptrObj;
    Py_TYPE(self)->tp_free(self);
}


static PyObject * PyVoice_filter(PyVoice* self, PyObject* args)
{
    int freq;
    int retval;

    if (! PyArg_ParseTuple(args, "i", &freq))
        return Py_False;

    retval = (self->ptrObj)->filter(freq);

    return Py_BuildValue("i",retval);
}


static PyMethodDef PyVoice_methods[] = {
    { "filter", (PyCFunction)PyVoice_filter,    METH_VARARGS,       "filter the mem voice" },
    {NULL}  /* Sentinel */
};

static PyTypeObject PyVoiceType = { PyVarObject_HEAD_INIT(NULL, 0)
                                    "voice.Voice"   /* tp_name */
                                };


PyMODINIT_FUNC PyInit_voice(void)
// create the module
{
    PyObject* m;

    PyVoiceType.tp_new = PyType_GenericNew;
    PyVoiceType.tp_basicsize=sizeof(PyVoice);
    PyVoiceType.tp_dealloc=(destructor) PyVoice_dealloc;
    PyVoiceType.tp_flags=Py_TPFLAGS_DEFAULT;
    PyVoiceType.tp_doc="Voice objects";
    PyVoiceType.tp_methods=PyVoice_methods;
    //~ PyVoiceType.tp_members=Noddy_members;
    PyVoiceType.tp_init=(initproc)PyVoice_init;

    if (PyType_Ready(&PyVoiceType) < 0)
        return NULL;

    m = PyModule_Create(&voicemodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&PyVoiceType);
    PyModule_AddObject(m, "Voice", (PyObject *)&PyVoiceType); // Add Voice object to the module
    return m;
}

.

distutils file : setup.py

from distutils.core import setup, Extension

setup(name='voicePkg', version='1.0',  \
      ext_modules=[Extension('voice', ['voiceWrapper.cpp','Voice.cpp'])])

.

python test file : test.py

import voice

v=voice.Voice(512)
result=v.filter(5)
print('result='+str(result))

.

and magic :

sudo python3 setup.py install
python3 test.py

Output is :

c++ constructor of voice
c++ voice filter method
result=6

Enjoy !

Doom

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.