Cython:如何使用 C++ 类的用户定义转换?

Posted

技术标签:

【中文标题】Cython:如何使用 C++ 类的用户定义转换?【英文标题】:Cython: How user-defined conversion of C++-classes can be used? 【发布时间】:2019-12-30 23:13:09 【问题描述】:

Cython 的 documentation 似乎对如何包装 user-defined conversion 保持沉默。

例如,当下面的 c++ 代码打印1(即true、here live)时:

#include <iostream>

struct X
    operator bool() const return true;    
;

int main() 
    X x;
    std::cout << x << "\n";

它在 Cython 中的“等价物”:

%%cython  -+
cdef extern from *:
    """
    struct X 
        //implicit conversion
        operator bool() const  return true; 
    ;
    """
    cdef cppclass X:
        operator bool()  # ERROR HERE

def testit():
    cdef X x;
    print(x) # implicit cast, "should" print True

没有得到 cythonized 并显示以下错误消息(在标有 ERROR HERE 的行中):

'operator' 不是类型标识符

如何从 Cython 使用用户定义的转换,如果不是,有什么解决方法?

【问题讨论】:

【参考方案1】:

只看bool案例:

    我不相信print(x) 应该将其转换为布尔值。 print(x) 寻找到 Python 对象的转换(好吧,bool 可以转换为 Python 对象,但这有点间接)。 Python 本身仅在相当有限的情况下使用__bool__(Python 2 中的__nonzero__),例如在if 语句中,并且 Cython 通常遵循 Python 行为作为规则。因此我将测试代码更改为

    def testit():
        cdef X x
        if x:
            print("is True")
        else:
            print("if False")
    

    operator bool() 给出错误

    'operator' 不是类型标识符

    我假设它需要像所有其他 C++ 函数一样以返回类型开始(即 operator 没有特殊情况)。这行得通(有点……见下一点……):

    bool operator bool()
    

    这个语法就是tested for in Cython's testsuite。

    但是,您确实需要在文件顶部执行 from libcpp cimport bool 以获得 C++ bool 类型。

如果您查看if x: 的转换源,它最终会是

__pyx_t_1 = __pyx_v_x.operator bool();
if (__pyx_t_1) 

operator bool 被显式调用(这在 Cython 中很常见),但在正确的位置使用,因此 Cython 清楚地了解它的用途。同样,如果您在没有定义运算符的情况下执行if x:,则会收到错误

“X”类型的对象没有属性“operator bool”

再次表明这是 Cython 的一个特性。

这里显然有一点文档错误,如果将来语法更改为更接近 C++,我不会 100% 感到惊讶,也许。


对于更一般的情况:目前是it looks like bool is the only type-conversion operator supported,因此您无法定义其他运算符。

【讨论】:

你可能是对的,期望转换为 python 对象太多了。 C++ 对自动使用转换运算符有点过于急切,因此通常最好使用 explicit 运算符来避免这种情况。 Cython 不会尝试自动执行此操作可能是明智的。【参考方案2】:

这只是对DavidW's answer 的一些补充。

正如已经指出的,Cython 仅支持 operator bool - 其他用户定义的转换,例如:

cdef cppclass X:
    int operator int()

将导致类似的错误消息

尚不支持重载运算符“int”。

一种可能的解决方法是不包装用户定义的转换,而是在需要时使用显式转换。例如:

%%cython  -+ -a
cdef extern from *:
    """
    struct X 
        //implicit conversion
        operator int() const  return 42; 
    ;
    """
    cdef cppclass X:
        pass # leave operator int() out

def testit():
    cdef X x;
    print(<int>x)

在调用testit 时编译并打印42。 Cython 不会在此处干预显式转换。

具有讽刺意味的是,上述解决方法不适用于operator bool()

%%cython  -+ -a
cdef extern from *:
    """
    struct X 
        //implicit conversion
        operator bool() const  return true; 
    ;
    """
    cdef cppclass X:
        pass # leave operator bool() out

def testit():
    cdef X x;
    if <bint>x:
       print(True)
    else:
       print(False)

导致错误信息:

“X”类型的对象没有属性“operator bool”

显然,此检查是 operator bool()-support 包的一部分。

但是,可以使用 cast to int 而不是 cast to bool/bint 来实现目标:

...
if <int>x:
...

但是,应该首选包裹operator bool()


简而言之:

    使用bint operator bool() 包装C++ 的operator bool() 不要为其他运算符包装和使用显式强制转换。

【讨论】:

以上是关于Cython:如何使用 C++ 类的用户定义转换?的主要内容,如果未能解决你的问题,请参考以下文章

如何从另一个包装的对象返回 Cython 中的包装 C++ 对象?

使用 Cython 时如何将一个 C++ 类(引用)传递给另一个?

如何在 python 包装中使用 unicode 字符串用于带有 cython 的 c++ 类?

如何在 IDE 中调试 Cython

在 Cython 中访问 C++ 类的私有成员变量/函数

使用 Cython 包装 C++ 类时处理指针