When I try to use the following Cython code, I get the error I posted at the end about operator() not being defined. It appears that when I try to use operators Cython does not interpret it as a member function (notice there is no member access in the C++ source). If I try to call prng.operator()() then Cython will fail translation.
Is something special needed 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()();
Some more information inspired by Ianh. It appears that operator() cannot be used when the object is stack allocated
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; }
};
Update: This should be fixed as of Cython 0.24 and later. I've left the workaround here for completeness.
After looking through the C++ compiler errors of examples like yours a little more, what appears to be happening is that Cython has a bug when overloading operator() for a stack allocated object.
It appears to be trying to call operator() as if it were some sort of function you had defined instead of as a method of the C++ object you have defined.
There are two possible workarounds.
You can either alias the call operator and give it a different name in Cython than in C.
You can also just allocate the object on the heap instead.
Depending on your use case, it could be a good idea to just patch the C file generated by Cython.
You'd basically just have to search for hanging calls to operator() change them to method calls on the proper C++ object.
I tried this with my example below and it worked, and it wasn't terribly difficult to trace which objects I needed to insert into the code.
This approach will work well if you are only trying to write Python bindings to the library and will not be making a large number of calls to operator() at the Cython level, but it could become an awful pain if you have a large amount of development you intend to do in Cython.
You could try bug reporting it too.
That would be good regardless of which route you take to get it working.
It seems like the sort of thing that should be easy to fix as well, but I'm not an expert on Cython's internals.
Here's 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 start 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
showing that the operator is working properly.
Now, if you want to do this for a stack allocated object, you can change the Cython interface as follows:
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.
Related
It is essentially an extension of this question - Usage of threadpoolexecutor in conjunction with cython's nogil
In this case my getArea2() method is slightly different
cdef int getArea2(self,double[:] p) nogil:
cdef int area
cdef SphericalPoint spoint
spoint = SphericalPoint()
area = 0
area = self.rect.getArea()
return area
and the .pxd declaration is slightly different
cdef extern from "Point.h":
cdef cppclass SphericalPoint(Point):
SphericalPoint() except +
double getCoordinate1()
double getCoordinate2()
void setCoordinate1(double lat)
void setCoordinate2(double lon)
With this I am getting these compilation errors. Any reason why the instantiation of the object is incorrect ?
I tried making spoint as a pointer and instantiating the object and it gives me the same error
cdef SphericalPoint *spoint
spoint = new SphericalPoint()
Error compiling Cython file:
cdef int getArea2(self,double[:] p) nogil:
cdef int area
cdef SphericalPoint spoint
spoint = SphericalPoint()
^
------------------------------------------------------------
test.pyx:78:30: Calling gil-requiring function not allowed without gil
It's a really simple issue: Cython assumes that any function needs the GIL unless you tell it otherwise. In the pxd declaration:
SphericalPoint() nogil except +
You probably want to add nogil to the other member functions too.
I initially thought the problem was the except + annotation. This causes Cython to catch any C++ exceptions and translate them to Python exceptions. This does require the GIL, but only if an exception is caught, and Cython takes care of this automatically, so don't worry about it. The alternative is the whole program crashing due to unhandled C++ exceptions so you do need to keep the except +.
In c++, I have Cell factory objects which return Cells that have CellSocket member variables. Don't worry about the details here, I'm just trying to illustrate the use-case for what I'm doing. The CellSockets can hold any type of variable with the help of boost::any. CellSockets have an overloaded operator<< that is basically an assignment operation, but with some other special stuff happening. So I can do stuff like this:
auto Adder = Cell::make_cell("Adder");
Adder.input_sockets[0] << "x";
Adder.input_sockets[1] << 1;
Adder.print(); //prints x+1
Now I'm trying to wrap the overloaded operator<< in Cython and running into some difficulty. I've exposed the overloaded operator in my .pxd like this:
cdef extern from "CellSocket.hpp" namespace "Quantum":
cdef cppclass CellSocket:
void operator<<[T](T)
Here's what this function signature looks like in the CellSocket.hpp file:
template<typename T>
void operator<<(const T& val)
In my .pyx, I try wrapping it into a python object like this:
cdef class QuCellSocket:
cdef CellSocket thisinst
def __cinit__(self):
self.thisinst = CellSocket()
def __lshift__(self, other):
self.thisinst << other
This compiles fine, but this happens when I try to use it:
>>> from Quantum import QuCellSocket
>>> a = QuCellSocket()
>>> a << 'x'.encode(encoding='utf_8', errors='strict')
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "Quantum.pyx", line 20, in Quantum.QuCellSocket.__lshift__ (/Users/Thomas/Documents/workspace/Quantum/QuantumAPI/src/Quantum.cpp:967)
AttributeError: 'Quantum.QuCellSocket' object has no attribute 'thisinst'
So what seems to be happening is that the __lshift__() function does not have access to anything in the c++ code space. Any ideas on what is the best way to do this? I'm trying to carry over the << assignment syntax into python.
I'm using cython 0.23.2, python 3.5, and gcc 5.2.0 on OSX El Capitan.
Edit:
So I've discovered I can get it working by doing something like this:
cdef class QuCellSocket:
cdef CellSocket thisinst
def __cinit__(self):
self.thisinst = CellSocket()
def lshift(self, other):
if type(other) is int:
self.thisinst << <int>other
elif type(other) is float:
self.thisinst << <float>other
elif isinstance(other, bytes):
self.thisinst << <string>other
else:
print("error: unsupported type!")
def __lshift__(self, other):
self.lshift(other)
But this is nasty. Any better ways?
Been breaking my head trying to pass vectors by reference to a C++ template method but all I get back is an empty list, apparently the parameter is being passed by value.
I am using Cython 0.18 & Python 2.7
Any ideas?
C++ side
class VectByRef
{
public:
VectByRef::VectByRef();
template<typename T>
void GetVector(T& var);
}
template<typename T>
void VectByRef::GetVector(T& var)
{
var.push_back(1);
var.push_back(2);
var.push_back(3);
}
Cython side
cdef extern from "VectByRef.h":
cdef cppclass VectByRef:
VectByRef() except
vector[cython.int] GetVector(vector[cython.int])
def getVector(self):
cdef vector[cython.int] resultVectInt
self._vectByRef.GetVector(<vector[cython.int]> resultVectInt)
print(resultVectInt) # The result is an empty list []
I'm not sure this actually answers the question, but I can't comment yet to get clarification.
When I tried to compile this I got an error:
error: no matching function for call to ‘VectByRef::GetVector(std::vector<int>)’
The error was resolved by changing the call on the Cython side to
.GetVector(<vector[cython.int]&> resultVectInt)
removing the type qualification altogether also worked:
.GetVector(resultVectInt)
Both of these gave the desired result as well: [1, 2, 3]
This was also using Python 2.7, with both Cython 0.17 & 0.19. So I'm guessing either something else is going on, or there's a specific bug with 0.18.
i'm currently writing a NLP application in python which requires a fast POS tagging implementation. the tagger has a c++ wrapper interface:
#include "POSTagger.h"
extern "C" POSTagger* initTagger(const char* fileName, bool Normalize,
double BeamThreshold, bool SentStartHeuristic,
int MaxBeamSize)
{
FILE *file = open_file(fileName, "rb");
POSTagger* tagger = new POSTagger(file, Normalize, BeamThreshold,
SentStartHeuristic, MaxBeamSize);
fclose(file);
return tagger;
}
extern "C" void getTags(POSTagger* tagger, char** words, int sentLen,
char** tags)
{
Sentence sent(words, sentLen);
tagger->annotate(sent);
for( size_t i=0; i<sent.token.size(); i++ )
tags[i] = strdup(tagger->tagmap.name(sent.token[i].tag));
}
extern "C" void destroyTagger(POSTagger* tagger) {
delete tagger;
}
i've never written a wrapper for c++ in python yet. so there are a couple of questions:
Can i store a custom C++ class instance in python?
i've never seen that before. all examples that i did go through returned only basic data types.
(this pos tagger has to be initialized with a language set, which requires some time to load it into the memory. therefore it is essential to only init and store it rather than rewriting the wrapper to create one for every tagging procedure and just return a tagged string)
If 1 is possible: what is the easiest way?
I'd suggest to use Cython for this purpose. Writing C/C++ extension-types is straight forward.
Unfortunately, I can't garuantee this code to be fully correct as I can't test it without the header you use.
# coding: utf-8
# file: postagger.pyx
cimport libc.stdlib as stdlib
cdef extern from "Python.h":
char* PyString_AsString(object)
cdef extern from "yourPOSTagger.c":
# note the syntax, to Cython the POSTagger class will be available
# as cPOSTagger using this construct
cppclass cPOSTagger "POSTagger":
# we only need the POSTagger type to be available to cython
# but no attributes, so we leave to ``pass``
pass
cPOSTagger* initTagger(char*, bint, double, bint, int)
void getTags(cPOSTagger*, char**, int, char**)
void destroyTagger(cPOSTagger*)
cdef class POSTagger:
""" Wraps the POSTagger.h ``POSTagger`` class. """
cdef cPOSTagger* tagger
def __init__(self, char* fileName, bint Normalize, double BeamTreshold,
bint SentStartHeuristic, int MaxBeamSize):
self.tagger = initTagger( fileName, Normalize, BeamTreshold,
SentStartHeuristic, MaxBeamSize )
if self.tagger == NULL:
raise MemoryError()
def __del__(self):
destroyTagger(self.tagger)
def getTags(self, tuple words, int sentLen):
if not words:
raise ValueError("'words' can not be None.")
cdef char** _words = <char**> stdlib.malloc(sizeof(char*) * len(words))
cdef int i = 0
for item in words:
if not isinstance(item, basestring):
stdlib.free(_words)
raise TypeError( "Element in tuple 'words' must be of type "
"``basestring``." )
_words[i] = PyString_AsString(item)
i += 1
cdef int nTags = len(words) # Really? Dunno..
cdef char** tags = <char**> stdlib.malloc(sizeof(char*) * len(words))
getTags(self.tagger, _words, sentLen, tags)
cdef list reval = []
cdef str temp
for 0 <= i < nTags:
temp = tags[i]
reval.append(temp)
stdlib.free(tags[i])
stdlib.free(tags)
return reval
You'd need this code to compile with the --cplus flag with Cython.
EDIT: Corrected code, Cython does no more give errors.
The easiest thing to do is to create an opaque type that can be pushed around in Python but that the client doesn't really have to care about.
I have a C++ library that has a Python wrapper (written with SWIG). This library allows executing small user-defined code (a callback), such as element-wise operations on a vector. I.e. instead of just a + you can do whatever arbitrary binary function. Right now this is accomplished by accepting a callable Python object for the binary function and calling it. It works, but is about 80 times slower than code that doesn't have to bounce up and down into Python at every iteration.
How would I write/build/import a Cython function could be passed into my C++ library so that it can be called directly by the C++ library?
Edit:
If I just stuck to C then I would write something like
EWise(double (*callback)(double, double))
EWise would then callback(10, 20); or such. I want callback to be written in Cython, using whatever name the user wants, and a pointer to it has to be passed to my C++ library through Python somehow. That somehow is where I'm unclear.
The trick with cython is in using the keyword public
cdef public double cython_function( double value, double value2 ):
return value + value2
Then the command cythonize <your_file.pyx> along with <your_file.c> will create header <your_file.h> that you can include.
Alternatively, you can create the header yourself:
#ifdef __cplusplus {
extern "C"
#endif
double cython_function( double value, double value2 );
#ifdef __cplusplus
}
#endif
Update:
Then with a little overlay from Python you can use ctypes's callback mechanism
func_type = CFUNCTYPE(c_double, c_double, c_double)
your_library.set_callback_function ( func_type(user_modules.cython_function) )
You can achieve that by doing pure cdef functions :
# declare the prototype of your function
ctypedef void (*callback_ptr)(int arg)
# declare your function as cdef
cdef void my_callback(int arg):
print 'doing some python here', arg
# now, you can use the cdef func as a callback
# in pure C !
cdef void run():
cdef callback_ptr p = my_callback
p(42)
if __name__ == '__main__':
run()
Note: you can use "cython -a" to see that they are no python code involved for the content of run. So it will work with your c library.
Embedding Python in Another Application may be useful reading.