下面的代码来自这个文件。它在 Linux 上可以正确编译,但在 OS X 上无法编译。
我想对自定义区间的向量进行排序:
stdsort(intervals.begin(), intervals.end(), compare_start_end)
我的比较函数如下:
cdef uint32_t compare_start_end(interval lhs, interval rhs):
if (lhs.start < rhs.start):
return <uint32_t> 1
elif (rhs.start < lhs.start):
return <uint32_t> 0
elif (lhs.end < rhs.end):
return <uint32_t> 1
else:
return <uint32_t> 0
我得到的错误如下:
In file included from epic2/src/read_bam.cpp:651:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4117:5: error: no matching function for call to '__sort'
__sort<_Comp_ref>(__first, __last, __comp);
^~~~~~~~~~~~~~~~~
epic2/src/read_bam.cpp:3305:12: note: in instantiation of function template specialization 'std::__1::sort<std::__1::__wrap_iter<__pyx_t_5epic2_3src_8read_bam_interval *>, unsigned int (__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)>'
requested here
std::sort<std::vector<__pyx_t_5epic2_3src_8read_bam_interval> ::iterator,uint32_t (__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)>(__pyx_v_intervals.begin(), __pyx_v_intervals.end(), __pyx_f_5epic2_3src_8read_bam_compare_start_end);
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3914:1: note: candidate function template not viable: no known conversion from 'unsigned int (*)(__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)' to 'unsigned int
(&)(__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)' for 3rd argument; dereference the argument with *
__sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
^
1 warning and 1 error generated.
问题似乎是类型之一。
我有
'unsigned int (*)(__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)'
但我的函数期望
unsigned int (&)(__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)
提示是尝试取消引用第三个参数,但这不起作用。
stdsort(intervals.begin(), intervals.end(), dereference(compare_start_end))
相反,它错误
Compiling epic2/src/read_bam.pyx because it changed.
[1/1] Cythonizing epic2/src/read_bam.pyx
Error compiling Cython file:
------------------------------------------------------------
...
intervals = dereference(it).second
five_ends = intvec()
if drop_duplicates:
stdsort(intervals.begin(), intervals.end(), dereference(compare_start_end))
^
你有什么建议吗? Ps. 以上在 Linux 上编译,但不在 macOS 上编译,所以代码很脆弱。
系统信息
macOS 莫哈韦, 10.14.6 (18G87(
gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
最小可重现示例
# minimal_example.pyx
from libc.stdint cimport uint32_t
from libcpp.algorithm cimport sort as stdsort
from libcpp.vector cimport vector
ctypedef struct interval:
uint32_t start
uint32_t end
ctypedef vector[uint32_t] intvec
ctypedef vector[interval] interval_vector
cdef uint32_t compare_start_end(interval lhs, interval rhs):
if (lhs.start < rhs.start):
return <uint32_t> 1
elif (rhs.start < lhs.start):
return <uint32_t> 0
elif (lhs.end < rhs.end):
return <uint32_t> 1
else:
return <uint32_t> 0
cdef test(interval_vector intervals):
stdsort(intervals.begin(), intervals.end(), compare_start_end)
编译方式:
folder_with_Python_h="/mnt/work/endrebak/software/anaconda/include/python3.7m/"
cython --cplus minimal_example.pyx
gcc -I $folder_with_Python_h -c minimal_example.cpp -o minimal_example.o -Ofast -Wall -std=c++11
在macOS上弹出相同的错误消息,但不在Linux上弹出。
我尝试过的其他命令,给出相同的结果:
g++ -I /Users/endrebakkenstovner/anaconda3/include/python3.6m/ -stdlib=libc++ -c minimal_example.cpp -o minimal_example.o -Ofast -Wall
gcc -I /Users/endrebakkenstovner/anaconda3/include/python3.6m/ -c minimal_example.cpp -o minimal_example.o -Ofast -Wall -lc++
尝试更改 Cython 代码
在 cdef 测试之前添加这些行
cdef extern from "<algorithm>" namespace "std":
void stdsort(...)
结果(在 Linux 和 macOS 上(
Error compiling Cython file:
------------------------------------------------------------
...
cdef extern from "<algorithm>" namespace "std":
void stdsort(...)
cdef test(interval_vector intervals):
stdsort(intervals.begin(), intervals.end(), compare_start_end)
^
------------------------------------------------------------
minimal_example.pyx:30:11: ambiguous overloaded method
根据注释在 stdsort(...( 的末尾添加"sort"(linux 和 macOS(:
Error compiling Cython file:
------------------------------------------------------------
...
else:
return <uint32_t> 0
cdef extern from "<algorithm>" namespace "std":
void stdsort(...) "sort"
^
------------------------------------------------------------
基本问题是 Cython 坚持指定模板参数。而不是生成如下所示的 C 代码:
std::sort(__pyx_v_intervals.begin(), __pyx_v_intervals.end(), __pyx_f_5epic2_3src_8read_bam_compare_start_end);
它生成
std::sort<std::vector<__pyx_t_5epic2_3src_8read_bam_interval> ::iterator,uint32_t (__pyx_t_5epic2_3src_8read_bam_interval, __pyx_t_5epic2_3src_8read_bam_interval)>(__pyx_v_intervals.begin(), __pyx_v_intervals.end(), __pyx_f_5epic2_3src_8read_bam_compare_start_end);
一般来说,在C++最好让C++弄清楚模板参数。在这种情况下,我认为 Cython 可能搞砸了函数指针参数。
解决方案是不要告诉Cython你有一个模板函数。这涉及自己重新包装函数,而不是使用 Cython 的 libcpp 包装器。一种选择是只指定所有类型 - 类型不必完全匹配,但必须足够接近,以便 Cython 认为它可以传递正确的参数:
cdef extern from "<algorithn>" namespace "std":
# code is untested because this isn't the solution I used...
void sort(vector[interval].iterator, vector[interval].iterator,
uint32_t (*)(interval, interval))
我更喜欢只使用...
.这是为了包装C varargs函数,如printf
,你可以传递任何东西,但它在这里也运行得很好:
cdef extern from "<algorithm>" namespace "std":
void sort(...)
# to rename to stdsort do
void stdsort "sort"(...)
最终结果是 Cython 不再试图告诉C++模板参数应该是什么。
std::unique
有点复杂,因为它具有返回类型。因此,Cython 需要知道至少一个模板参数。幸运的是,我很确定只有最后一个参数会导致问题,所以你可以放心地告诉 Cython 第一个参数是一个模板:
cdef extern from "<algorithm>" namespace "std":
Iter unique[Iter](Iter, Iter, ...)