Cython cannot use operator ()

When I try to use the following Cython code, I get the error that I posted at the end operator()

, which is not defined. It looks like when I try to use operators, Cython doesn't interpret it as a member function (note that there is no member access in the C ++ source). If I try to call prng.operator()()

Cython will fail the translation.

Is there anything special you need to use operator overloading in Cython?

import numpy as np
cimport numpy as np

cdef extern from "ratchet.hpp" namespace "ratchet::detail":
    cdef cppclass Ratchet:
        Ratchet()
        unsigned long get64()

cdef extern from "float.hpp" namespace "prng":
    cdef cppclass FloatPRNG[T]:
        double operator()()



cdef FloatPRNG[Ratchet] prng

def ratchet_arr(np.ndarray[np.float64_t, ndim=1] A):
    cdef unsigned int i
    for i in range(len(A)):
        A[i] = prng()


def ratchet_arr(np.ndarray[np.float64_t, ndim=2] A):
    cdef unsigned int i, j
    for i in range(len(A)):
        for j in range(len(A[0])):
            A[i][j] = prng()

      

ratchet.cpp: In function β€˜PyObject* __pyx_pf_7ratchet_ratchet_arr(PyObject*, PyArrayObject*)’: ratchet.cpp:1343:162: error: β€˜operator()’ not defined *__Pyx_BufPtrStrided1d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_A.rcbuffer->pybuffer.buf, __pyx_t_3, __pyx_pybuffernd_A.diminfo[0].strides) = operator()();

Additional information inspired by Ian. It looks like operator()

it cannot be used when the object is on the stack

cat thing.pyx
cdef extern from 'thing.hpp':
    cdef cppclass Thing:
        Thing(int)
        Thing()
        int operator()()

# When this function doesn't exist, thing.so compiles fine
cpdef ff():
    cdef Thing t
    return t()

cpdef gg(x=None):
    cdef Thing* t
    if x:
        t = new Thing(x)
    else:
        t = new Thing()
    try:
        return t[0]()
    finally:
        del t

cat thing.hpp
#pragma once

class Thing {
    int val;

    public:
    Thing(int v): val(v) {}
    Thing() : val(4) {}

    int operator()() { return val; }
};

      

+3


source to share


1 answer


Update: This should be fixed as of Cython 0.24 and later. I've left a workaround here for completeness.


After looking at the C ++ compiler errors in examples like yours, there is a bit more that looks like Cython has an overload error operator()

for a stack allocated object. It seems to be trying to call operator()

as if it were some function that you defined, not as a method of the C ++ object that you defined. There are two possible workarounds. You can either alias the call statement or give it a different name in Cython than in C. You can also just allocate an object on the heap.

Depending on your use case, it might be a good idea to just fix the C file Cython generated. You will just have to look for hang-up calls operator()

to change them to method calls on the corresponding C ++ object. I tried it with my example below and it worked and it was tricky to keep track of which objects I need to insert into the code. This approach will work well if you are only trying to write Python bindings to the library and not making a lot of calls operator()

at the Cython level, but it can be a terrible pain if you have a large number of things that you intend to do in Keaton.

You can also try reporting a bug. That would be fine no matter which route you take to get it to work. This looks like something that should be easy to fix, but I'm not a Cython internals expert.

Here is a minimal working example of how to use operator()

for a heap allocated object in Cython. It works for me on Cython 0.21.

Thing.hpp

#pragma once

class Thing{
    public:
        int val;
        Thing(int);
        int operator()(int);};

      

Thing.cpp

#include "Thing.hpp"

Thing::Thing(int val){
    this->val = val;}

int Thing::operator()(int num){
    return this->val + num;}

      

Thing.pxd

cdef extern from "Thing.hpp":
    cdef cppclass Thing:
        Thing(int val) nogil
        int operator()(int num) nogil

      

test_thing.pyx

from Thing cimport Thing

cpdef test_thing(int val, int num):
    cdef Thing* t = new Thing(val)
    print "initialized thing"
    print "called thing."
    print "value was: ", t[0](num)
    del t
    print "deleted thing"

      



setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from os import system

# First compile an object file containing the Thing class.
system('g++ -c Thing.cpp -o Thing.o')

ext_modules = [Extension('test_thing',
                         sources=['test_thing.pyx'],
                         language='c++',
                         extra_link_args=['Thing.o'])]

# Build the extension.
setup(name = 'cname',
      packages = ['cname'],
      cmdclass = {'build_ext': build_ext},
      ext_modules = ext_modules)

      

After running the setup file, I launch the Python interpreter in the same directory and run

from test_thing import test_thing
test_thing(1, 2)

      

and it prints the output

initialized thing
called thing.
value was:  3
deleted thing

      

shows that the operator is working correctly.

Now, if you want to do this for a dedicated object stack, you can change the Cython interface like this:

Thing.pxd

cdef extern from "Thing.hpp":
    cdef cppclass Thing:
        Thing(int val) nogil
        int call "operator()"(int num) nogil

      

test_thing.pyx

from Thing cimport Thing

cpdef test_thing(int val, int num):
    cdef Thing t = Thing(val)
    print "initialized thing"
    print "called thing."
    print "value was: ", t.call(num)
    print "thing is deleted when it goes out of scope."

      

The C ++ files and setup file can still be used as they are.

+3


source







All Articles