Cython: Как обернуть функцию C ++, которая возвращает объект C ++?

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

Вкратце, мне нужно обернуть функцию FooBar, которая возвращает объект класса пользовательского типа.

Вот Bar.h:

#include <cstddef> // For size_t #include <vector> /* data returned by function FooBar()*/ class Bar { public: size_t X; std::vector<size_t> Y; std::vector<double> Z; std::vector<double> M; std::vector<size_t> N; }; Bar FooBar(const std::vector<double> & O, size_t P, size_t Q); 

И PyBar.pyx:

 from libcpp.vector cimport vector cdef extern from "Bar.h": cdef cppclass Bar: size_t X vector[size_t] Y vector[double] Z vector[double] M vector[size_t] N cdef Bar FooBar(const vector[double] & O, size_t P, size_t Q) cdef class PyBar: cdef Bar *thisptr # hold a C++ instance which we're wrapping def __cinit__(self, O, P, Q): C_Bar = FooBar(O, P, Q) self.thisptr = &C_Bar def __dealloc__(self): del self.thisptr 

Актуальный вопрос: Это даже правильный подход к тому, что я хочу сделать? Для справки, если бы я просто попытался обернуть класс сам по себе, у меня не было проблем: я могу импортировать модуль, создавать объекты с помощью PyBar (), а базовые методы C, реализованные в классе, будут работать. Проблема заключается в попытке обернуть функцию, возвращающую объекты класса C ++. В дикой природе я никогда не хочу создавать представление PyBar любого объекта Bar, который не был создан FooBar, так что это подход, который я решил после многократной царапин.

2 Solutions collect form web for “Cython: Как обернуть функцию C ++, которая возвращает объект C ++?”

Что касается первой части проблемы, я думаю, что более элегантным изменением было бы определение FooBar следующим образом:

 Bar* FooBar(const std::vector<double> & O, size_t P, size_t Q); 

и вернуть ему «новый» выделенный указатель. Я думаю, что в вашем оригинальном Cython-коде __cinit__ вы создадите стек, выделенную стеком, возьмите указатель на это, а затем истечет срок действия, что приведет к возможной катастрофе.

Альтернативным решением, которое могло бы работать, было бы сохранение FooBar возвращаемого бара, изменение PyBar, чтобы оно начиналось

 cdef class PyBar: cdef Bar this_obj def __cinit__(self, O, P, Q): self.this_obj = FooBar(O,P,Q) 

т.е. хранит объект, а не указатель. Нет необходимости в __dealloc__ .

Я не знаю о неопределенной ошибке символа …

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

Может быть, кто-то с большим опытом написания кода на C ++ может придумать что-то лучшее, но ради потомства:

Лично мне было проще изменить FooBar () так, чтобы он был членом функции Bar: вместо возврата объекта Bar теперь он изменяет экземпляр, из которого он вызван. Затем, когда обертывание Bar в Cython, я не выставляю FooBar () как метод класса, но я вызываю метод в конструкторе для объекта Python (и, следовательно, соответствующего C ++). Это работает для меня, потому что, как я уже сказал, я действительно только когда-либо намерен заниматься объектами Bar, которые были инициализированы некоторым набором значений с помощью FooBar ().

В конце концов, я выбрал этот подход с помощью конструктора копирования (который позволил бы мне инициализировать новый Python / соответствующий объект C ++ Bar из существующего объекта Bar, созданного FooBar), потому что он казался мне более читаемым. Преимущество подхода конструктора копирования состояло бы в том, что нужно было бы изменить определение класса Bar в C (добавив конструктор копирования), что может быть предпочтительным, если вам действительно неудобно изменять реализацию FooBar (). В моем случае, поскольку объекты Bar иногда могут содержать очень большие векторы, конструктор копирования также казался плохой идеей по соображениям производительности.

Вот мой последний код:

bar.h:

 #include <cstddef> // For size_t #include <vector> class Bar { public: size_t X; std::vector<size_t> Y; std::vector<double> Z; std::vector<double> M; std::vector<size_t> N; void FooBar(const std::vector<double> & O, size_t P, size_t Q); ClusterResult(){} }; 

PyBar.pyx:

 from libcpp.vector cimport vector cdef extern from "Bar.h": cdef cppclass Bar: size_t X vector[size_t] Y vector[double] Z vector[double] M vector[size_t] N Bar() void FooBar(const vector[double] & O, size_t P, size_t Q) cdef class PyBar: cdef Bar *thisptr # hold a C++ instance which we're wrapping def __cinit__(self, O, P, Q): self.thisptr = new Bar() self.thisptr.FooBar(O, P, Q) def __dealloc__(self): del self.thisptr #Below, I implement the public attributes as get/setable properties. #could have written get/set functions, but this seems more Pythonic. property X: def __get__(self): return self.thisptr.X def __set__(self, X): self.thisptr.X = X property Y: def __get__(self): return self.thisptr.Y def __set__(self, Y): self.thisptr.Y = Y property Z: def __get__(self): return self.thisptr.Z def __set__(self, Z): self.thisptr.centers = Z property M: def __get__(self): return self.thisptr.M def __set__(self, size): self.thisptr.M = M property N: def __get__(self): return self.thisptr.N def __set__(self, size): self.thisptr.N = N 

Рефакторинг FooBar () Реализация:

Затем я переписал реализацию FooBar () в Bar.cpp, изменив тип возврата на void и заменив объект Bar result ранее возвращенный функцией для this . Например (и явным образом я использую это для ясности):

 Bar FooBar(const std::vector<double> & O, size_t P, size_t Q) { Bar result = new Bar(); result.X = P + 1; result.Z = std::sort(O.begin()+1, O.end()); const size_t newParam = Q + 2; someOtherFunction(newParam, result); ... } 

что-то вроде этого:

 void Bar::FooBar(const std::vector<double> & O, size_t P, size_t Q) { this->X = P + 1; this->Z = std::sort(O.begin()+1, O.end()); const size_t newParam = Q + 2; someOtherFunction(newParam, *this); ... } 
  • Использование Cython для предоставления функциональности другому приложению
  • Как установить cython
  • Эффективно вычисляемая сумма квадратов разностей
  • Использование Cython с Django. Имеет ли это смысл?
  • Обработка строк в Cython
  • ИмпортError на подмодуль Cython
  • Использование Cython для переноса шаблона c ++ для принятия любого массива numpy
  • Разница между np.int, np.int_, int и np.int_t в cython?
  • Python - лучший язык программирования в мире.