如何在“语言=c++”模式下使用C复数?

2024-04-15

我的大部分库都是用 Cython 在“正常”C 模式下编写的。到目前为止,我很少需要任何 C++ 功能,但总是假设(有时确实如此!)如果我愿意,我可以将一个模块切换到 C++ 模式。

所以我有大约 10 个 C 模式模块和 1 个 C++ 模式模块。

现在的问题是 Cython 如何seems处理复数定义。在 C 模式下,它假设我认为 C 复数,而在 C++ 模式下,它假设我认为 C++ 复数。我读过它们现在可能是相同的,但无论如何 Cython 抱怨它们不是:

openChargeState/utility/cheb.cpp:2895:35: error: cannot convert ‘__pyx_t_double_complex {aka std::complex<double>}’ to ‘__complex__ double’ for argument ‘1’ to ‘double cabs(__complex__ double)’
 __pyx_t_5 = ((cabs(__pyx_v_num) == INFINITY) != 0);

在这种情况下,我尝试使用 C 模式模块中定义的 cab,并从 C++ 模式模块中调用它。

我知道有一些明显的解决方法(现在我只是不使用 C++ 模式;我想使用向量,而暂时使用较慢的 Python 列表)。

有没有办法告诉我的 C++ 模式模块使用 C 复数,或者告诉它它们是相同的?如果我在 C++ 模式模块中找不到 ctypedef C 复数的工作方法...或者还有其他解决方案吗?


EDIT:DavidW 和 ead 的评论提出了一些合理的事情。首先是最小工作示例。

setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize

extra_compile_args=['-O3']
compdir = {'language_level' : '3'}

extensions = cythonize([
                        Extension("cmod", ["cmod.pyx"]),
                        Extension("cppmod", ["cppmod.pyx"], language='c++')
                       ], 
                       compiler_directives = compdir
                      )

setup(cmdclass = {'build_ext': build_ext},
      ext_modules = extensions
     )

import cppmod

cmod.pyx

cdef double complex c_complex_fun(double complex xx):
    return xx**2    

cmod.pxd

cdef double complex c_complex_fun(double complex xx)

cdef extern from "complex.h":
    double cabs(double complex zz) nogil

cpmod.pyx

cimport cmod

cdef double complex cpp_complex_fun(double complex xx):
    return cmod.c_complex_fun(xx)*abs(xx)   # cmod.cabs(xx) doesn't work here

print(cpp_complex_fun(5.5))

然后只需使用 python3 setup.py build_ext --inplace 进行编译即可。

现在有趣的部分是(如代码中所写)只有“间接”导入的 c 函数有问题,就我而言cabs。所以建议只使用abs实际上确实有帮助,但我仍然不明白底层逻辑。我希望我不会在其他问题中遇到这个问题。我暂时留下这个问题。也许有人知道发生了什么事。


您的问题与以下事实无关:一个模块编译为 C 扩展,另一个模块编译为 C++ 扩展 - 人们可以轻松地仅在 C++ 扩展中重现该问题:

%%cython -+ 
cdef extern from "complex.h": 
    double cabs(double complex zz) nogil

def cpp_complex_fun(double complex xx):
    return cabs(xx)

结果是您的错误消息:

错误:无法转换__pyx_t_double_complex {aka std::complex<double>} to __complex__ double为了论证1 to double cabs(__complex__ double)

问题是复数……嗯,很复杂。 Cython的策略(可以查here https://github.com/cython/cython/blob/e6315294395ad44e667a804710938ee799575296/Cython/Utility/Complex.c#L4 and here https://github.com/cython/cython/blob/e6315294395ad44e667a804710938ee799575296/Cython/Utility/Complex.c#L56)处理复数的方法是使用 C/CPP 中的可用实现,如果没有找到,则使用手写的后备:

#if !defined(CYTHON_CCOMPLEX)
  #if defined(__cplusplus)
    #define CYTHON_CCOMPLEX 1
  #elif defined(_Complex_I)
    #define CYTHON_CCOMPLEX 1
  #else
    #define CYTHON_CCOMPLEX 0
  #endif
#endif
....
#if CYTHON_CCOMPLEX
  #ifdef __cplusplus
    typedef ::std::complex< double > __pyx_t_double_complex;
  #else
    typedef double _Complex __pyx_t_double_complex;
  #endif
#else
    typedef struct { double real, imag; } __pyx_t_double_complex;
#endif

如果是 C++ 扩展,Cython 的double complex被翻译成std::complex<double>因此不能用cabs( double complex z )因为std::complex<double> isn't double complex.

所以实际上,这是你的“错”:你对 Cython 撒了谎并告诉他,cabs有签名double cabs(std::complex<double> z),但这还不足以欺骗 c++ 编译器。

这意味着,在 c++ 模块中std::abs(std::complex<double>)可以使用,或者只是 Cython 的/Python 的abs,即自动翻译 https://github.com/cython/cython/blob/e6315294395ad44e667a804710938ee799575296/Cython/Utility/Complex.c#L130到正确的功能(但这对于所有标准功能来说都是不可能的)。


如果是 C 扩展名,因为您已经包含了complex.h作为所谓的“早期包含”cdef extern from "complex.h",因此对于上述定义_Complex_I被定义并且 Cython 的复合体成为别名double complex因此cabs可以使用。


对于 Cython 来说,正确的做法可能是始终使用默认的后备,并且用户应该能够选择所需的实现(double complex or std::complex<double>)明确。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在“语言=c++”模式下使用C复数? 的相关文章

随机推荐