Ошибка компиляции при использовании метода перемещения в Cython

Мой вопрос аналогичен заданному здесь - Передача вектора C++ в Numpy через Cython без автоматического копирования и управления памятью Я также получаю ошибку сегментации, но прежде чем я смогу это исправить, у меня есть некоторые ошибки компиляции, которые необходимо устранить с помощью метода перемещения в Cython.

Это самый простой пример, который у меня есть (просто расширение примера Rectangle, предоставленного в сети

Что я хочу сделать Мой код C++ возвращает очередь точек. Он также может возвращать вектор точек. Подойдет любой контейнер

В коде cython я хочу сохранить возвращенную очередь точек (по одной для каждой итерации цикла for) в коллекции (например, вектор или очередь). Эта коллекция будет переменной экземпляра.

Позже я хочу перебрать коллекцию и преобразовать эту очередь точек в список списков Python. Вот где я считаю, что я получаю ошибку сегментации.

Точка.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);

Rectangle.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

Я считаю, что устанавливаю С++ 17 в setup.pyx

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++',
                 )]

расширения = 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);

person gansub    schedule 04.08.2019    source источник
comment
Итак, просто чтобы прояснить проблему с минимально воспроизводимым примером: мы не знаем, что находится в Point.h. Также есть несколько опечаток в шаблонах (например, deque<Point>> - посчитайте ‹›...), которые предполагают, что это не совсем тот код, который генерирует ошибку. Я думаю, что знаю, в чем проблема, поэтому я отвечу, как только проверю (если это проблема)   -  person DavidW    schedule 04.08.2019
comment
@DavidW, да, исправлена ​​​​проблема с deque‹Point› !! Извини за это !   -  person gansub    schedule 04.08.2019


Ответы (1)


Ниже приведена немного упрощенная версия, которую я использовал для воспроизведения вашей проблемы. Я просто включаю его в качестве иллюстрации того, как еще больше сократить ваш пример - обратите внимание, что мне не нужно использовать файлы C++ - я могу просто включить код непосредственно в файл pyx, поместив его в строку документации.

#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++ вывести тип шаблона. По причинам, которые я не совсем понимаю, это, кажется, часто генерирует неправильный код.

У меня есть два с половиной решения:

  1. Не говорите Cython, что move — это шаблонная функция. Вместо этого просто скажите ему о перегрузках, которые вы хотите:

    cdef extern from "<utility>" namespace "std" nogil:
        deque[Point] move(deque[Point])
    

    Это самый простой, я думаю, и, вероятно, мой подход.

  2. Если вы избегаете создания временного area, тогда код C++ работает с шаблоном:

    self.colOfPoints = move(self.getArea())
    

    Я подозреваю, что в этом случае вам может даже не понадобиться move - С++, вероятно, все равно автоматически использует оператор присваивания перемещения.

  3. В этом ответе утверждается, что он содержит небольшую оболочку, помогающую Cython вызывать правильный move (он также немного упустил фактическую проблему на вопрос, на который он отвечал...). Я не проверял это сам, но, возможно, стоит попробовать, если вам нужен шаблонный ход.

person DavidW    schedule 04.08.2019
comment
Да, спасибо. Я обязательно проверю это. Я задал этот вопрос на мета, и любые предложения будут оценены - title="какой минимальный воспроизводимый проверяемый пример для cython">meta.stackoverflow.com/questions/388123/ - person gansub; 04.08.2019
comment
ОК... Не слишком беспокойтесь о отрицательных отзывах на мете - они не учитываются! - person DavidW; 04.08.2019
comment
не уверен, что вы заинтересованы, но я разместил этот вопрос в группах cython google - groups.google.com/forum/, потому что я чувствовал, что он стал ориентированным на обсуждение для SO. В конечном итоге я хотел бы попросить и здесь об этом, но суть в том, что я не могу выполнить итерацию после операции перемещения. - person gansub; 23.08.2019
comment
Я предполагаю, что мне понадобится std:move_iterator, а не std::move. Если это так, то об этом еще не спрашивали на SO, то есть как использовать move_iterator с Cython, и это может быть правильным вопросом, если я могу обрезать лишний материал в вопросе. - person gansub; 23.08.2019
comment
По сути, то, что Роберт говорит в публикации группы Cython google, является сутью этого ответа SO - c" title="эффективный способ возврата стандартного вектора в c">stackoverflow.com/questions/15704565/, т. е. оптимизация возвращаемого значения. Компилятор перемещает его без перемещения. Если это так, то не знаю, почему это не работает для меня - person gansub; 23.08.2019
comment
На самом деле я не хочу подписываться на обсуждение групп Google, но: очень внимательно посмотрите на приращения вашего итератора, чтобы убедиться, что вы выбрали правильный. Я думаю, что у вас есть бесконечный цикл. - person DavidW; 23.08.2019