【问题标题】:Cython cannot use operator()Cython 不能使用 operator()
【发布时间】:2014-09-17 19:15:04
【问题描述】:

当我尝试使用以下 Cython 代码时,我收到了我在最后发布的关于 operator() 未定义的错误。看来,当我尝试使用运算符时,Cython 不会将其解释为成员函数(注意 C++ 源代码中没有成员访问)。如果我尝试调用 prng.operator()(),那么 Cython 将无法翻译。

在 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()();

受 Ianh 启发的更多信息。看来operator()在对象被堆栈分配时不能使用

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; }
};

【问题讨论】:

  • 好问题...最好包含完整的错误消息...您是在告诉 Cython 编译器这是 C++ 应用程序而不是 C 应用程序吗?
  • @SaulloCastro 谢谢你的建议。我告诉 Cython 生成 C++ 代码。这个错误实际上来自 gcc。 Cython 翻译成功。 gcc 在这里抱怨是对的,因为没有与 operator() 关联的对象被调用。如果我改为调用 prng 的不同成员函数,则代码中会有成员尊重。
  • 这看起来像是一个尚未实现的功能。我尝试整理一个简单的示例,但它不起作用。不过,this question 的答案有一个简单的解决方法。如果您愿意,我可以发布一个更完整的示例。不过确实不太理想。这可能会很好地出现在邮件列表中。
  • 嗯。不,文档确实说现在支持运算符重载。 operator() 没有任何警告。这绝对是一个错误。
  • 啊,我好像说得太早了。我仍然没有看到您的代码有问题,但我会发布一个工作示例。希望这会有所帮助。

标签: c++ numpy cython


【解决方案1】:

更新:这应该在 Cython 0.24 及更高版本中得到修复。为了完整起见,我将解决方法留在这里。


在仔细查看像您这样的示例的 C++ 编译器错误之后,似乎正在发生的事情是 Cython 在为堆栈分配的对象重载 operator() 时存在错误。 它似乎试图调用operator(),就好像它是您定义的某种函数,而不是您定义的C++ 对象的方法。 有两种可能的解决方法。 您可以给呼叫操作员起别名,并在 Cython 中给它一个不同于 C 中的名称。 您也可以只在堆上分配对象。

根据您的用例,最好只修补 Cython 生成的 C 文件。 您基本上只需要搜索对 operator() 的挂起调用,将它们更改为正确 C++ 对象上的方法调用。 我在下面的示例中尝试了此操作,并且成功了,并且跟踪我需要将哪些对象插入到代码中并不难。 如果您只是尝试将 Python 绑定写入库,并且不会在 Cython 级别对 operator() 进行大量调用,这种方法将很有效,但如果您有大量的您打算在 Cython 中进行的开发。

您也可以尝试报告错误。 无论您采取哪种方式使其正常工作,这都会很好。 这似乎也应该很容易解决,但我不是 Cython 内部的专家。

这是一个最小的工作示例,说明如何在 Cython 中将operator() 用于堆分配对象。它适用于 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)

运行设置文件后,我在同一目录下启动Python解释器并运行

from test_thing import test_thing
test_thing(1, 2)

它会打印输出

initialized thing
called thing.
value was:  3
deleted thing

显示操作者工作正常。

现在,如果要对堆栈分配的对象执行此操作,可以按如下方式更改 Cython 接口:

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."

C++ 文件和安装文件仍可照原样使用。

【讨论】:

  • 感谢您的示例!也适用于我的机器。看看我添加到我的问题中的信息。我认为您对我的启发足以找到我们的代码之间的差异并找到问题所在。也许您可以在有关堆栈分配对象的答案中添加一些内容(如果您可以确认这是问题所在)?
  • @chewsocks 感谢您的评论。我又回去看了一遍。这似乎是一个仅适用于堆栈分配对象的错误。我不完全确定为什么。在 Cython 用户列表中显示这将是一个很好的错误。更熟悉 Cython 核心的人可能能够轻松修复它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-12
  • 2022-01-05
  • 1970-01-01
  • 2021-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多