Writing a Python Module Using C / API and C ++ Classes

I'm new to writing custom Python modules and I'm a little confused about how Capsules work. I am using Python 2.7.6 from a system OSX install and am trying to use Capsules (as recommended for Python> 2.7) to pass pointers (before they used PyCObject to do this). At the moment my code is not working and I would like to get an idea of ​​how things should be taken here. The code needs to define the LuscherClm class, and I want to be able to do the following:

>>> c40=Luscher(4,0)
>>>
>>> c40(0.12)
>>> <print the result of the evaluation>

      

First question: at this point I will need to do something like:

>>> c40=Luscher.init(4,0)
>>>
>>> c40.eval(0.12)
Segfault 

      

So my first question is, how do I modify the method table to have more operator-style casts instead of the init and eval member functions.

However my code has other problems and here is the relevant part (C ++ base class runs smoothly, I use it in production a lot):

Destructor:

//destructor
static void clm_destruct(PyObject* capsule){
    void* ptr=PyCapsule_GetPointer(capsule,"zetfunc");
    Zetafunc* zetptr=static_cast<Zetafunc*>(ptr);
    delete zetptr;
    return;
}

      

Constructor: It returns a pointer to the capsule. I don't know if this is correct. Because in this case, when I call clm = LuscherClm.init (l, m), the clm object is PyCapsule and has no eval attribute, so I cannot call clm.eval (x). How should this be handled?

//constructor
static PyObject* clm_init(PyObject* self, PyObject *args){
    //return value
    PyObject* result=NULL;

    //parse variables
    unsigned int lval=0;
    int mval=0;
    if(!PyArg_ParseTuple(args,"li",&lval,&mval)){
        ::std::cout << "Please specify l and m!" << ::std::endl;
        return result;
    }

    //class instance:
    Zetafunc* zetfunc=new Zetafunc(lval,mval);
    instanceCapsule=PyCapsule_New(static_cast<void*>   (zetfunc),"zetfunc",&clm_destruct);
    return instanceCapsule;
}

      

So how is the capsule passed to the evaluation function? the code below is not correct as I haven't updated it after moving from CObjects to Capsules. Should the capsule be a global variable (I don't like that) or how do I pass it to the evaluation function? Or I'll call it myself, but what really is now?

//evaluate the function
static PyObject* clm_evaluate(PyObject* self, PyObject* args){
    //get the PyCObject from the capsule:
    void* tmpzetfunc=PyCapsule_GetPointer(instanceCapsule,"zetfunc");
    if (PyErr_Occurred()){
        std::cerr << "Some Error occured!" << std::endl;
        return NULL;
    }
    Zetafunc* zetfunc=static_cast< Zetafunc* >(tmpzetfunc);
    //parse value:
    double x;
    if(!PyArg_ParseTuple(args,"d",&x)){
        std::cerr << "Specify a number at which you want to evaluate the function" << std::endl;
        return NULL;
    }
    double result=(*zetfunc)(x).re();

    //return the result as a packed function:
    return Py_BuildValue("d",result);
}

//methods
static PyMethodDef LuscherClmMethods[] = {
    {"init",  clm_init, METH_VARARGS, "Initialize clm class!"},
    {"eval", clm_evaluate, METH_VARARGS, "Evaluate the Zeta-Function!"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

      

Python <3 initialization function:

PyMODINIT_FUNC
initLuscherClm(void)
{
    PyObject *m = Py_InitModule("LuscherClm", LuscherClmMethods);
    return;
}

      

Can you explain to me what is wrong and why? I would like to stay away from SWIG or boost if possible as this module should be easily portable and I want to avoid having to install additional packages every time I want to use it elsewhere. Next: what is the overhead caused by the C / API when calling the function? I need to call it order O (10 ^ 6) times, and I would still like it to be fast.

Okay, now I'm using boost.python, but I'm getting a segfault when running object.eval (). This is my procedure now:

BOOST_PYTHON_MODULE(threevecd)
{
    class_< threevec<double> >("threevecd",init<double,double,double>());
}

BOOST_PYTHON_MODULE(LuscherClm)
{
    class_<Zetafunc>("LuscherClm",init<int,int, optional<double,threevec<double>,double,int> >())
    .def("eval",&Zetafunc::operator(),return_value_policy<return_by_value>());
    boost::python::to_python_converter<dcomplex,dcomplex_to_python_object>();
}

      

dcomplex is my own complex number implementation. So I had to write a converter:

struct dcomplex_to_python_object
{
    static PyObject* convert(dcomplex const& comp)
    {
        if(fabs(comp.im())<std::numeric_limits<double>::epsilon()){
            boost::python::object result=boost::python::object(complex<double>(comp.re(),comp.im()));
            return boost::python::incref(result.ptr());
        }
        else{
            return Py_BuildValue("d",comp.re());
        }
    }
};

      

Complex128 is a numeric extension not understood by boost. So my questions are: 1) how can I return a complex number as python datatype (python complex standard type?) 2) Why am I getting a segfault. My result in my test file is real, so it should specify else by default. I assume the pointer ends out of scope, and that's about it. But even in the if case (where I'll take care of the ref-increments), these are segfaults. Can anyone help me with the type conversion problem?

Thanks Thorsten

+3


source to share


1 answer


OK I understood. The following converter does the job:

struct dcomplex_to_python_object
{
    static PyObject* convert(dcomplex const& comp)
    {
        PyObject* result;
        if(std::abs(comp.im())<=std::numeric_limits<double>::epsilon()){
            result=PyFloat_FromDouble(comp.re());
        }
        else{
            result=PyComplex_FromDoubles(comp.re(),comp.im());
        }
        Py_INCREF(result);
        return result;
    }
};

      



Using this converter and post from Wouter, I suppose my question was answered. Thanks to

0


source







All Articles