4

I am new to the business of writing custom Python modules and I am a bit confused how Capsules work. I use Python 2.7.6 from the system OSX installation and try to use Capsules (as recommended for Python > 2.7) for passing pointers around (before they used PyCObject for that). My code does not work at the moment and I would like to get some insights how things should be handled in principle here. The code should define a class LuscherClm and I want be able to do the following:

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

First question: at the moment I would have to do something like:

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

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

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

The destructor:

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

The constructor: it returns the pointer to the capsule. I do not know whether this is correct. Because in this case when I call, clm=LuscherClm.init(l,m), the clm object is a PyCapsule and has no attribute eval so that I cannot call clm.eval(x) on that. 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 evaluate function? the code below is not correct since I have not updated it after moving from CObjects to Capsules. Shall the capsule be a global variable (I do not like that) or how can I pass it to the evaluation function? Or shall I call it on self, but what is self at the moment?

//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 initialisation 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, since this module should be easily portable and I want to avoid having to install additional packages every time I want to use it somewhere else. Further: what is the overhead produced by the C/API in calling the function? I need to call it an order of O(10^6) times and I would still like it to be fast.

Ok, I am using boost.python now but I get a segfault when I run object.eval(). That 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 numpy extension which is not understood by boost. So my questions are: 1) how can I return a complex number as a python datatype (is complex a standard python type?) 2) Why do I get a segfault. My result in my testcase is real so it should default to the else statement. I guess that the pointer runs out of scope and thats it. But even in the if-case (where I take care about ref-increments), it segfaults. Can someone help me with the type conversion issue?

Thanks Thorsten

5
  • Ok, since I got no response so far and I really do not like the API documentation, I want to use boost.python. However, it seems trickier than I though. That is my whole method 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>(); } Commented May 2, 2015 at 0:25
  • In Python, __init__() is invoked after constructing the instance, so that means you can use 'self' without problems, since it is a valid object (although uninitialised) at that point. Commented May 3, 2015 at 13:07
  • 1
    re. calling the instances directly: i guess you'll want to populate the tp_call slot (__call__() in python). Commented May 3, 2015 at 13:10
  • Ok, thanks. But how can I get around my segfault problem? The ideal case would be to return a complex number in Python. Commented May 5, 2015 at 19:19
  • Have you tried the Py_complex type? docs.python.org/2.7/c-api/complex.html Commented May 6, 2015 at 9:44

1 Answer 1

1

Ok, I got it. 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 the post by Wouter, I suppose my question is answered. Thanks

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.