如何在 'language=c++' 模式下使用 C 复数?
How to use C complex numbers in 'language=c++' mode?
我的大部分库都是用 "normal" C 模式下的 Cython 编写的。到目前为止,我很少需要任何 C++ 功能,但总是假设(有时确实如此!)如果我愿意,我可以只为一个模块切换到 C++ 模式。
所以我有 10 多个 C 模式模块和 1 个 C++ 模式模块。
现在的问题是 Cython 似乎 如何处理复数定义。在 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 模式模块中定义的出租车,并从 C++ 模式模块调用它。
我知道有一些明显的解决方法(现在我只是不使用 C++ 模式;我想使用向量,而不是现在使用较慢的 Python 列表)。
有没有办法告诉我的 C++ 模式模块使用 C 复数,或者告诉它它们是相同的?如果我在我的 C++ 模式模块中找不到 ctypedef C 复数的工作方法...或者还有其他解决方案吗?
编辑: 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
cppmod.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.
编译
现在有趣的部分是(如代码中所写)只有 "indirectly" 导入的 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)
导致您的错误消息:
error: cannot convert __pyx_t_double_complex {aka
std::complex<double>}
to __complex__ double
for argument 1
to
double cabs(__complex__ double)
问题是复数……好吧,很复杂。 Cython 处理复数的策略(可以查找 here and here)是使用 C/CPP 中的可用实现,如果找到 none,则使用手写回退:
#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>
不是 [=16] =].
所以实际上,这是你的 "fault":你对 Cython 撒谎并告诉他,cabs
具有签名 double cabs(std::complex<double> z)
,但这不足以欺骗 c++-编译器。
这意味着,在 c++ 模块中可以使用 std::abs(std::complex<double>)
,或者只是 Cython's/Python 的 abs
,即 automatically translated 到正确的函数(然而,这对于所有标准功能都是不可能的。
在 C 扩展的情况下,因为您已经将 complex.h
作为所谓的 "early include" 包含在 cdef extern from "complex.h"
中,因此对于上述定义 _Complex_I
已定义并且 Cython 的复合体成为 double complex
的别名,因此可以使用 cabs
。
对于 Cython 来说,正确的做法可能是始终默认使用回退,并且用户应该能够明确选择所需的实现(double complex
或 std::complex<double>
)。
我的大部分库都是用 "normal" C 模式下的 Cython 编写的。到目前为止,我很少需要任何 C++ 功能,但总是假设(有时确实如此!)如果我愿意,我可以只为一个模块切换到 C++ 模式。
所以我有 10 多个 C 模式模块和 1 个 C++ 模式模块。
现在的问题是 Cython 似乎 如何处理复数定义。在 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 模式模块中定义的出租车,并从 C++ 模式模块调用它。
我知道有一些明显的解决方法(现在我只是不使用 C++ 模式;我想使用向量,而不是现在使用较慢的 Python 列表)。
有没有办法告诉我的 C++ 模式模块使用 C 复数,或者告诉它它们是相同的?如果我在我的 C++ 模式模块中找不到 ctypedef C 复数的工作方法...或者还有其他解决方案吗?
编辑: 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
cppmod.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.
编译现在有趣的部分是(如代码中所写)只有 "indirectly" 导入的 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)
导致您的错误消息:
error: cannot convert
__pyx_t_double_complex {aka std::complex<double>}
to__complex__ double
for argument1
todouble cabs(__complex__ double)
问题是复数……好吧,很复杂。 Cython 处理复数的策略(可以查找 here and here)是使用 C/CPP 中的可用实现,如果找到 none,则使用手写回退:
#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>
不是 [=16] =].
所以实际上,这是你的 "fault":你对 Cython 撒谎并告诉他,cabs
具有签名 double cabs(std::complex<double> z)
,但这不足以欺骗 c++-编译器。
这意味着,在 c++ 模块中可以使用 std::abs(std::complex<double>)
,或者只是 Cython's/Python 的 abs
,即 automatically translated 到正确的函数(然而,这对于所有标准功能都是不可能的。
在 C 扩展的情况下,因为您已经将 complex.h
作为所谓的 "early include" 包含在 cdef extern from "complex.h"
中,因此对于上述定义 _Complex_I
已定义并且 Cython 的复合体成为 double complex
的别名,因此可以使用 cabs
。
对于 Cython 来说,正确的做法可能是始终默认使用回退,并且用户应该能够明确选择所需的实现(double complex
或 std::complex<double>
)。