Я довольно новичок в создании класса C ++, который я могу использовать из Python. Я просмотрел много постов на inte rnet. Будь то на StackOverflow, GIST, GitHub, ... Я также прочитал документацию, но я не уверен, как я могу решить мою проблему.
По сути, идея состоит в том, чтобы сделать это: http://www.speedupcode.com/c-class-in-python3/ Поскольку я хочу избежать бремени создания своего собственного python newtype , я подумал, что использование PyCapsule_New
и PyCapsule_GetPointer
, как в примере выше, может быть обходным решением, но, возможно, я ввел в заблуждение, и мне все еще нужно создать сложный тип данных.
Вот заголовок моего класса, из которого я хочу позвонить с python:
template<typename T>
class Graph {
public:
Graph(const vector3D<T>& image, const std::string& similarity, size_t d) : img(image) {...}
component<T> method1(const int k, const bool post_processing=true);
private:
caller_map<T> cmap;
vector3D<T> img; // input image with 3 channels
caller<T> sim; // similarity function
size_t h; // height of the image
size_t w; // width of the image
size_t n_vertices; // number of pixels in the input image
size_t conn; // radius for the number of connected pixels
vector1D<edge<T>> edges; // graph = vector of edges
void create_graph(size_t d);
tuple2 find(vector2D<subset>& subsets, tuple2 i);
void unite(vector2D<subset>& subsets, tuple2 x, tuple2 y);
};
Итак, как вы можете видеть, мой класс содержит сложные структуры. vector1D
- это просто std::vector
, но edge - это структура, определяемая
template<typename T>
struct edge {
tuple2 src;
tuple2 dst;
T weight;
};
, а некоторые методы используют другие сложные структуры.
В любом случае, я создал собственную привязку Python. Здесь я только поставил соответствующие функции. Я создал constructor
следующим образом:
static PyObject *construct(PyObject *self, PyObject *args, PyObject *kwargs) {
// Arguments passed from Python
PyArrayObject* arr = nullptr;
// Default if arguments not given
const char* sim = "2000"; // similarity function used
const size_t conn = 1; // Number of neighbor pixels to consider
char *keywords[] = {
"image",
"similarity",
"d",
nullptr
};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|sI:vGraph", keywords, PyArray_Converter, &arr, &sim, &conn)) {
// Will need to DECRF(arr) somewhere?
return nullptr;
}
set<string> sim_strings = {"1976", "1994", "2000"};
if (sim_strings.find(sim) == sim_strings.end()) {
PyErr_SetString(PyExc_ValueError, "This similarity function does not exist");
Py_RETURN_NONE;
}
// Parse the 3D numpy array to vector3D
vector3D<float> img = parse_PyArrayFloat<float>(arr);
// call the Constructor
Graph<float>* graph = new Graph<float>(img, sim, conn);
// Create Python capsule with a pointer to the `Graph` object
PyObject* graphCapsule = PyCapsule_New((void * ) graph, "graphptr", vgraph_destructor);
// int success = PyCapsule_SetPointer(graphCapsule, (void *)graph);
// Return the Python capsule with the pointer to `Graph` object
// return Py_BuildValue("O", graphCapsule);
return graphCapsule;
}
Во время отладки моего кода я вижу, что мой конструктор возвращает мой объект graphCapsule, и тогда он отличается от nullptr
.
. Я создаю свою method1
функцию следующим образом:
static PyObject *method1(PyObject *self, PyObject *args) {
// Capsule with the pointer to `Graph` object
PyObject* graphCapsule_;
// Default parameters of the method1 function
size_t k = 300;
bool post_processing = true;
if (!PyArg_ParseTuple(args, "O|Ip", &graphCapsule_, &k, &post_processing)) {
return nullptr;
}
// Get the pointer to `Graph` object
Graph<float>* graph = reinterpret_cast<Graph<float>* >(PyCapsule_GetPointer(graphCapsule_, "graphptr"));
// Call method1
component<float> ctov = graph->method1(k, post_processing);
// Convert component<float> to a Python dict (bad because we need to copy?)
PyObject* result = parse_component<float>(ctov);
return result;
}
Когда я все скомпилирую, у меня будет библиотека vgraph.so
, и я буду вызывать ее из Python, используя:
import vgraph
import numpy as np
import scipy.misc
class Vgraph():
def __init__(self, img, similarity, d):
self.graphCapsule = vgraph.construct(img, similarity, d)
def method1(self, k=150, post_processing=True):
vgraph.method1(self.graphCapsule, k, post_processing)
if __name__ == "__main__":
img = scipy.misc.imread("pic.jpg")
img = scipy.misc.imresize(img, (512, 512)) / 255
g = Vgraph(lab_img, "1976", d=1)
cc = g.method1(k=150, post_processing=False)
Идея состоит в том, что я сохраняю PyObject pointer
, возвращаемый vgraph.construct
. Затем я вызываю method1
, передавая PyObject pointer
int k = 150
и bool postprocessing
.
. Поэтому в реализации C ++ *method1
я использую: !PyArg_ParseTuple(args, "O|Ip", &graphCapsule_, &k, &post_processing)
для анализа этих 3 объектов. .
Проблема в том, что, когда я отлаживаю, я восстанавливаю k=150
и post_processing=False
, которые исходят из того, как я вызываю C ++ из Python ... Я также получая 0X0
, то есть nullptr
в переменной graphCapsule_
...
Так что, очевидно, остальная часть кода не может работать ...
Я думал что PyObject *
является указателем на мой граф типа Graph<float> *
, поэтому я ожидал, что ParseTuple восстановит мой PyObject *
указатель, который я затем смогу использовать в PyCapsule_GetPointer
для получения моего объекта.
Как мне заставить мой код работать? Нужно ли мне определять свой собственный PyObject, чтобы ParseTuple понимал его? Есть ли более простой способ сделать это?
Большое спасибо!
Примечание : если я нарушу свой код python, я вижу, что мой график g
содержит PyObject
с адресом, на который он указывает, и именем объекта (здесь graphtr
), поэтому я ожидал, что мой код заработает ...
Note2 : Если мне нужно создать свой собственный newtype
, я видел следующее сообщение: Как обернуть объект C ++ с помощью чистого Python API-расширения (python3)? , но я думаю из-за сложного объекты моего класса, это будет довольно сложно?