Я хотел бы использовать в коде cython C++ std::priority_queue, где каждый элемент должен быть структурой или классом, но я не знаю, как определить компаратор.

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

simple.pyx

# distutils: language = c++
cimport cython
from libcpp.queue cimport priority_queue

def testIntPriorityQueue():
    cdef priority_queue[int] q
    q.push(5)
    q.push(15)
    q.push(7)

    print('q.top (after pushing 5, 15, 7) = ',q.top())

    q.pop()
    print('q.top (after popping one item) = ',q.top())

< EM> main.py

import simple 

def main():
    simple.testIntPriorityQueue()
   
main()

Когда я выполняю этот код, priority_queue после правильного нажатия 5, 15 и 7 имеет сверху наибольшее значение (15) (поскольку оно возвращается q.top), а после извлечения одного элемента q.top возвращает 7, который стал наибольшим после удаление 15.

Теперь я хотел бы иметь подобное поведение, но с элементами, помещаемыми в очередь, которые не являются объектами стандартного типа, а чем-то более сложным.

Я попытался определить такую ​​структуру со следующими вещами:

simple.pxd

cimport cython
ctypedef struct myType:
    int key
    float a
    float b

simple.pyx

# distutils: language = c++
cimport cython
from libc.stdlib cimport  malloc, free
from libcpp.queue cimport priority_queue
from libcpp.vector cimport vector

def testPriorityQueue():
    cdef myType * item
    
    cdef priority_queue[myType*] q

    item = <myType *> malloc(sizeof(myType)) 
    item.key = 20
    item.a = 3.
    item.b = 4.
    q.push(item)

    item = <myType *> malloc(sizeof(myType)) 
    item.key = 10
    item.a = 1.
    item.b = 2.
    q.push(item)
    
    item = q.top()
    print('q.top',item.key,item.a,item.b)

Теперь ни от компилятора, ни во время выполнения не выдается никакой ошибки, но, конечно, порядок в priority_queue не может быть определен, и из отпечатков я получаю, что элемент сверху является последним.

В C++, чтобы сделать priority_queue для сортировки отправленных элементов в такой ситуации, метод сравнения должен быть предоставлен в качестве аргумента для создания очереди, но это, как я нашел, должно быть сделано через перегрузку оператора less, как в этот пример С++:

struct ToastCompare
{
    bool operator()(const Toast &t1, const Toast &t2) const
    {
        int t1value = t1.bread * 1000 + t1.butter;
        int t2value = t2.bread * 1000 + t2.butter;
        return t1value < t2value;
    }
};

Я попытался определить аналогичную функцию в cython, добавив эти строки в файл pyx:

simple.pyx

cdef extern from *:
    """
    struct itemCompare{
        bool operator() (self, myType *t1, myType *t2) const
        {
            return t1.key < t2.key;
        }
    };
    """
    ctypedef struct itemCompare
cdef priority_queue[myType*,vector[myType*],itemCompare] q

Но во время компиляции я получаю ошибку "шаблонный тип Priority_queue получает 1 аргумент, получил 3".

Возможно ли в cython использовать контейнер Priority_queue С++ с объектами настраиваемого класса?


Пытаясь следовать предложению @ead, я попытался изменить код следующим образом:

simple.pyx

cdef extern from *:
    """
    bool operator<(const myType t1, const myType t2)
    { return t1.key < t2.key;}
    """
def testPriorityQueue():
    cdef priority_queue[myType*] q

Оставить в simple.pxd

cimport cython
from libcpp cimport bool
ctypedef struct myType:
    int key
    float a
    float b

Но он не может скомпилировать предоставление:

simple.cpp: 694: 26: ошибка: «myType» не называет оператор типа bool <(const myType t1, const myType t2)

Поэтому я попытался переместить определение структуры в код C-verbatim и удалить его из simple.pxd.

simple.pyx

def extern from *:
    """
    typedef struct myType{
        int key;
        float a;
        float b;       
    } myType;

    bool operator<(const myType t1, const myType t2)
    {return t1.key < t2.key;}
    """
    ctypedef struct myType
def testPriorityQueue():
    cdef myType * item
    cdef priority_queue[myType*] q

    item = <myType *> malloc(sizeof(myType)) 

И теперь ошибка компиляции:

Невозможно принять размер неполного типа "myType"

Если я оставлю определение структуры также в файле pxd, но удалю ctypedef struct myType из pyx, ошибки компиляции не возникнет, но очередь приоритетов не сортирует элементы, как если бы не было перегрузки.

Где моя ошибка?

2
Alessandro De Maio 23 Апр 2020 в 13:16
Чтобы открыть этот вопрос, потребуется время - всегда лучше задать вопрос правильно с самого начала.
 – 
ead
24 Апр 2020 в 00:11
В то же время есть некоторые недоразумения: 1) вам нужно переопределить оператор less (stackoverflow.com/q/3006413/5769463 ) не оператор(). 2) помещать в priority_queue сами свои объекты, а не указатели (если объекты такие маленькие, как вы представили) - никаких проблем с управлением памятью (да и новичку проще разобраться). 3) Напишите оператор struct + на C++ (вы можете использовать verbatim-C-code (cython.readthedocs.io/en/latest/src/userguide/…) и оберните его с помощью Cython — это проще, чем пытаться определить их в Cython.
 – 
ead
24 Апр 2020 в 00:13
Спасибо за ваш ответ. Это мой первый пост здесь, поэтому я понял, что мне пришлось отредактировать его после закрытия вместо того, чтобы публиковать новый вопрос. Извиняюсь
 – 
Alessandro De Maio
24 Апр 2020 в 13:37
Что касается ваших предложений: 1) хорошо для меньшего оператора 2) это был только пример, объекты, которые мне нужно поместить в приоритетную очередь, больше, чем те, которые я использовал в примере, но я постараюсь следовать вашим предложениям 3) Я пытался сделать то, что вы предлагаете, но теперь cythonizing моего pyx останавливается с ошибкой «тип шаблона Priority_queue получает 1 аргумент, получил 3», поэтому я не знаю, является ли это пределом C++ обертывания std :queue в Cython или нет
 – 
Alessandro De Maio
24 Апр 2020 в 13:41
Вы можете обновить вопрос своей текущей версией, потому что, не видя кода, трудно сказать, что пошло не так.
 – 
ead
24 Апр 2020 в 13:44

1 ответ

Ответом на мой вопрос было использование следующего файла pxd:

cimport cython
from libcpp cimport bool

cdef extern from *:
    """
    typedef struct {
        int key;
        float a;
        float b;       
    } myType;

    bool operator<(const myType t1, const myType t2) {
        return t1.key < t2.key;
    }
    """
    ctypedef struct myType:
        int key
        float a
        float b

Спасибо @ead за помощь

1
Alessandro De Maio 1 Май 2020 в 13:01