在 Cython 中使用 move 方法时出现编译错误
Posted
技术标签:
【中文标题】在 Cython 中使用 move 方法时出现编译错误【英文标题】:Compilation error when using the move method in Cython 【发布时间】:2019-08-04 07:58:44 【问题描述】:我的问题与此处提出的问题类似 - Passing C++ vector to Numpy through Cython without copying and taking care of memory management automatically 我也遇到了分段错误,但在修复它之前,我需要使用 Cython 中的 move 方法处理一些编译错误。
这是我拥有的最简单的示例(只是网络中提供的 Rectangle 示例的扩展
我想做的事 我的 C++ 代码返回点的双端队列。它也可以返回一个点向量。任何容器都可以
在 cython 代码中,我想将返回的点的双端队列(for 循环的每次迭代一个)存储在集合(例如向量或双端队列)中。该集合将是一个实例变量。
稍后我想遍历集合并将 deque 的 deque 点转换为 python 列表列表。这是我相信我遇到分段错误的地方。
Point.h
#ifndef POINT_H
#define POINT_H
class Point
private:
double coordinate1,coordinate2;
public:
virtual double getCoordinate1() const;
virtual double getCoordinate2() const ;
virtual void setCoordinate1(double coordinate1);
virtual void setCoordinate2(double coordinate2);
;
矩形.h
#include <deque>
#include "Point.h"
using std:deque;
deque<Point> getAllPoints(Point query);
矩形.cpp
include "Rectangle.h"
deque<Point> Rectangle::getAllPoints(Point query)
deque<Point> deq;
for (int i = 0;i < 10000; ++i)
deq.push_back(query);
return deq;
注意与链接的问题不同,我不是返回地址而是返回参考
rect.pxd
cdef extern from "<utility>" namespace "std" nogil:
T move[T](T) #
cdef extern from "Point.h":
cdef cppclass Point:
Point() nogil except +
double getCoordinate1()
double getCoordinate2()
void setCoordinate1(double coordinate1) nogil
void setCoordinate2(double coordinate2) nogil
cdef cppclass SphericalPoint(Point):
SphericalPoint() nogil except +
double getCoordinate1()
double getCoordinate2()
void setCoordinate1(double lat) nogil
void setCoordinate2(double lon) nogil
cdef extern from "Rectangle.h" namespace "shapes":
cdef cppclass Rectangle:
Rectangle(int, int, int, int) except + nogil
deque[Point] getArea(Point p) nogil
最后
rect.pyx
cdef class PyRectangle:
cdef Rectangle *rect
cdef deque[Point] colOfPoints
def __cinit__(self, int x0, int y0, int x1, int y1):
self.rect = new Rectangle(x0, y0, x1, y1)
self.colOfPoints = deque[Point]()
def performCalc(self,maxDistance,chunk):
cdef deque[Point] area
cdef double[:,:] gPoints
gPoints = memoryview(chunk)
for i in range(0,len(gPoints)):
with nogil:
area = self.getArea(gPoints[i])
self.colOfPoints = move(area)
cdef deque[Point] getArea(self,double[:] p) nogil:
cdef deque[Point] area
area = self.rect.getArea(point)
return area
我相信我在 setup.pyx 中设置了 C++ 17
setup.py
os.environ['CFLAGS'] = '-O3 -Wall -std=c++17'
ext_modules = [Extension("rect",
["rect.pyx","Rectangle.cpp"],
include_dirs=['/usr/local/include'],
extra_link_args=["-std=c++17"],
language='c++',
)]
extensions = cythonize(ext_modules, language_level = "3")
我收到这些编译错误
rect.cpp: In function ‘PyObject* __pyx_pf_4rect_11PyRectangle_6performCalc(__pyx_obj_4rect_PyRectangle*, PyObject*, PyObject*)’:
rect.cpp:3781:81: error: no matching function for call to ‘move<std::deque<Point, std::allocator<Point> > >(std::deque<Point>&)’
__pyx_v_self->colOfPoints = std::move<std::deque<Point> >(__pyx_v_area);
^
In file included from /usr/include/c++/7 /bits/nested_exception.h:40:0,
from /usr/include/c++/7/exception:143,
from /usr/include/c++/7/ios:39,
from rect.cpp:632:
/usr/include/c++/7/bits/move.h:98:5: note: candidate: template<class _Tp> constexpr typename std::remove_reference< <template-parameter-1-1> >::type&& std::move(_Tp&&)
move(_Tp&& __t) noexcept
^~~~
/usr/include/c++/7/bits/move.h:98:5: note: template argument deduction/substitution failed:
rect.cpp:3781:81: note: cannot convert ‘__pyx_v_area’ (type ‘std::deque<Point>’) to type ‘std::deque<Point>&&’
__pyx_v_self->colOfPoints = std::move<std::deque<Point> >(__pyx_v_area);
^
In file included from /usr/include/c++/7/bits/char_traits.h:39:0,
from /usr/include/c++/7/ios:40,
from rect.cpp:632:
/usr/include/c++/7/bits/stl_algobase.h:479:5: note: candidate: template<class _II, class _OI> _OI std::move(_II, _II, _OI)
move(_II __first, _II __last, _OI __result)
^~~~
/usr/include/c++/7/bits/stl_algobase.h:479:5: note: template argument deduction/substitution failed:
rect.cpp:3781:81: note: candidate expects 3 arguments, 1 provided
__pyx_v_self->colOfPoints = std::move<std::deque<Point> >(__pyx_v_area);
^
In file included from /usr/include/c++/7/deque:66:0,
from rect.cpp:636:
/usr/include/c++/7/bits/deque.tcc:1048:5: note: candidate: template<class _Tp> std::_Deque_iterator<_Tp, _Tp&, _Tp*> std::move(std::_Deque_iterator<_Tp, const _Tp&, const _Tp*>, std::_Deque_iterator<_Tp, const _Tp&, const _Tp*>, std::_Deque_iterator<_Tp, _Tp&, _Tp*>)
move(_Deque_iterator<_Tp, const _Tp&, const _Tp*> __first,
^~~~
/usr/include/c++/7/bits/deque.tcc:1048:5: note: template argument deduction/substitution failed:
rect.cpp:3781:81: note: candidate expects 3 arguments, 1 provided
__pyx_v_self->colOfPoints = std::move<std::deque<Point> >(__pyx_v_area);
^
In file included from /usr/include/c++/7/deque:64:0,
from rect.cpp:636:
/usr/include/c++/7/bits/stl_deque.h:424:5: note: candidate: template<class _Tp> std::_Deque_iterator<_Tp, _Tp&, _Tp*> std::move(std::_Deque_iterator<_Tp, _Tp&, _Tp*>, std::_Deque_iterator<_Tp, _Tp&, _Tp*>, std::_Deque_iterator<_Tp, _Tp&, _Tp*>)
move(_Deque_iterator<_Tp, _Tp&, _Tp*> __first,
^~~~
/usr/include/c++/7/bits/stl_deque.h:424:5: note: template argument deduction/substitution failed:
rect.cpp:3781:81: note: candidate expects 3 arguments, 1 provided
__pyx_v_self->colOfPoints = std::move<std::deque<Point> >(__pyx_v_area);
【问题讨论】:
所以只是为了澄清minimal reproducible example 问题:我们不知道“Point.h”中有什么。还有一些模板拼写错误(例如deque<Point>>
- 数 ...),这表明这不是生成错误的代码。我想我知道问题是什么,所以我会在测试后回答(如果是问题)
@DavidW 是的,也修复了 deque以下是我用来重现您的问题的稍微简化的版本。我只是将它包括在内,以说明如何进一步减少您的示例 - 请注意,我不需要使用 C++ 文件 - 我可以将代码直接包含在 pyx 文件中,方法是将其放入 docstring 中。
#distutils: language = c++
from libcpp.deque cimport deque
cdef extern from *:
"""
#include <deque>
using std::deque;
class Point;
deque<Point> getAllPoints()
deque<Point> deq;
for (int i=0; i<10000; ++i)
deq.push_back(Point);
return deq;
"""
cdef cppclass Point:
pass
deque[Point] getAllPoints()
cdef extern from "<utility>" namespace "std" nogil:
T move[T](T)
cdef class PyRectange:
cdef deque[Point] colOfPoints
def something(self):
cdef deque[Point] area = self.getArea()
self.colOfPoints = move(area)
cdef deque[Point] getArea(self):
return getAllPoints()
基本问题是,当 Cython 为模板生成 C++ 代码时,它编写 std::move<deque<Point>>(area)
而不是 std::move(area)
并让 C++ 推断模板类型。由于我不完全理解的原因,这似乎在很多时候会生成错误的代码。
我有两个半的解决方案:
不要告诉 Cython move
是一个模板函数。而只是告诉它你想要的重载:
cdef extern from "<utility>" namespace "std" nogil:
deque[Point] move(deque[Point])
这是我认为最简单的方法,也可能是我的方法。
如果您避免创建临时的area
,那么 C++ 代码可以使用模板:
self.colOfPoints = move(self.getArea())
我怀疑在这种情况下你甚至可能不需要move
- C++ 可能会自动使用移动赋值运算符。
This answer 声称提出了一个小包装器,以帮助 Cython 调用正确的 move
(它也略微错过了它正在回答的问题的实际问题......)。我自己没有测试过,但如果你想要一个模板化的移动,它可能值得一试。
【讨论】:
好的,谢谢。我一定会检查出来的。我在 meta 上问了这个问题,任何建议都将不胜感激 - meta.***.com/questions/388123/… 好的...不要太担心meta上的否决 - 他们不算数! 不确定您是否感兴趣,但我在 cython google 群组上发布了这个问题 - groups.google.com/forum/…,因为我觉得它已成为面向 SO 的讨论。我最终也很想在这里问它,但要点是在移动操作后我无法迭代。 我的猜测是我需要一个 std:move_iterator 而不是 std::move。如果是这种情况,那么尚未在 SO 上提出此问题,即如何将 move_iterator 与 Cython 一起使用,并且可能是一个有效的问题,前提是我可以裁剪问题中的多余材料 基本上罗伯特在 Cython 谷歌小组帖子中所说的是这个 SO 答案的要点 - ***.com/questions/15704565/… 即返回值优化。编译器在不移动的情况下移动它。如果是这种情况,不知道为什么它对我不起作用以上是关于在 Cython 中使用 move 方法时出现编译错误的主要内容,如果未能解决你的问题,请参考以下文章