странный сбой расширения cython, ошибка cython?

Кто-нибудь может объяснить, почему это расширение приводит к сбою Python?
Я уже много часов схожу с ума по этому поводу и не могу объяснить.

Я упростил свой код, чтобы он отображал только те строки, которые необходимы для воспроизведения сбоя:

У меня есть очень простой класс C++ в «test_class.h», который использует пару типов opencv cv::Mat и cv::KeyPoint:

class TestClass
{
public:
    TestClass();
    cv::Mat& get_current_frame_descriptors();

    std::vector<cv::KeyPoint> _current_frame_points;
    cv::Mat _current_frame_descriptors;
};

"test_class.cpp"

TestClass::TestClass()
{
}

Mat& TestClass::get_current_frame_descriptors()
{
    return _current_frame_descriptors;
}

Затем у меня есть обертка cython в «test_class.pyx»:

 from libcpp.vector cimport vector

 cdef extern from "opencv2/core/core.hpp" namespace "cv":
    cdef cppclass Mat:
        Mat()
        int dims, rows, cols, flags

cdef extern from "test_class.h":       
    cdef cppclass TClass "TestClass":
        TClass()
        Mat _current_frame_descriptors        
        Mat& get_current_frame_descriptors()


cdef class TestClass:
    cdef TClass *TClass_ptr
    def __cinit__(self):
        self.TClass_ptr = new TClass()

    def get_descriptors1(self):
        m = self.TClass_ptr.get_current_frame_descriptors()
        print m.dims
        print m.rows
        print m.cols
        print m.flags

    def get_descriptors2(self):
        m = self.TClass_ptr._current_frame_descriptors
        print m.dims
        print m.rows
        print m.cols
        print m.flags

Обратите внимание, что TClass не объявляет _current_frame_points (вектор KeyPoints), так как нет необходимости воспроизводить сбой.

Я создаю расширение cython и тестирую его:

>>>import  test_class
>>>m = test_class.TestClass()

Opencv Mat _current_frame_descriptors пуст, поэтому размеры, строки и столбцы равны нулю:

>>>m.get_descriptors1() 
0
0
0
1124007936

>>>m.get_descriptors2() 

Это крашит питон!!!

Теперь, если я отменю объявление других _current_frame_descriptors и _current_frame_points в test_class.h, тогда я не получу никакого сбоя!!!!

class TestClass
{
public:
    TestClass();
    cv::Mat& get_current_frame_descriptors();

    cv::Mat _current_frame_descriptors;
    std::vector<cv::KeyPoint> _current_frame_points;    
};

Теперь я пересобираю C++ (я создаю его как библиотеку, на которую затем ссылаюсь с расширением cython). Я перестраиваю расширение cython и тестирую его.

>>>import  test_class
>>>m = test_class.TestClass()

Opencv Mat _current_frame_descriptors пуст, поэтому, когда я это делаю

>>>m.get_descriptors1() 
0
0
0
1124007936

>>>m.get_descriptors2() 
0
0
0
1124007936

Теперь я получаю правильный результат!!!

Как это возможно? это ошибка цитона? ошибка opencv? или ошибка С++? или я что-то не так делаю? Мой компилятор Visual Studio Express 2009


person martinako    schedule 27.09.2013    source источник
comment
Попробуйте объявить _current_frame_points в объявлении класса cython. AFAIK cython не будет читать определение из файла, что означает, что он не знает членов класса C++, которых нет в файле cython. Следовательно, при выполнении self.TClass_ptr._current_frame_descriptors он обращается к неправильному месту в памяти. Замена объявлений решает проблему, потому что в этом случае cython использует правильное местоположение. Этого не должно произойти, если вы добавите _current_frame_points в объявление Cython, так как в этот момент он сможет получить правильное место в памяти.   -  person Bakuriu    schedule 27.09.2013
comment
Я только что заметил. Обратите внимание, что TClass не объявляет _current_frame_points (вектор ключевых точек), поскольку нет необходимости воспроизводить сбой. что я не понимаю, что это значит. Вы имеете в виду, что когда вы do объявляете это, он не падает или все равно падает? Кроме того, когда вы объявляете это, вы объявляете членов в том же порядке или нет?   -  person Bakuriu    schedule 27.09.2013
comment
Спасибо за ваш комментарий. Я имел в виду, что он рухнул, когда _current_frame_points был в объявлении, и рухнул, когда я его удалил. По сути, я продолжал удалять вещи во время сбоя, пока не получил этот минимальный набор.   -  person martinako    schedule 28.09.2013
comment
Собственно, я нашел проблему. Это не ошибка. Я сам виноват, я смешивал библиотеки времени выполнения.   -  person martinako    schedule 28.09.2013


Ответы (1)


Я нашел причину проблемы. Никаких ошибок. Это была моя проблема. Я смешивал библиотеки времени выполнения :-( Месяц назад я начал переводить некоторый числовой код Python на C++, используя cython в качестве соединения для проверки согласованных результатов. Когда я создаю расширение cython, я использую это предупреждение компоновщика:

ССЫЛКА: предупреждение LNK4098: defaultlib «MSVCRTD» конфликтует с использованием других библиотек; используйте /NODEFAULTLIB:библиотека

Но я не знал, как его удалить, и код, похоже, работал. На самом деле я перевел довольно много кода в этих условиях, и до сих пор у меня не было проблем. Добавьте перерыв в несколько недель в середине, я совсем забыл об этом.

Мне пришлось добавить /MDd к extra_compile_args, чтобы соответствовать моей библиотеке C++. Тогда у меня возникла бы проблема, что мне нужен python27_d.exe. Однажды я попытался собрать это, но затем мне пришлось также собрать отладочную версию каждой библиотеки, которую я использую! невыполнимо!

Я нашел хитрость в Как отлаживать расширения C для Python в Windows Мне пришлось прокомментировать  #define Py_DEBUG в C:\Python27\include\pyconfig.h, а затем скопировать python27.exe в python27_d.exe Теперь я мог собирать с /MDd

После этого у меня перестают вылетать.

person martinako    schedule 28.09.2013