Dynamic sort comparator in Cython

Viewed 593

I need to generate a sort comparator using a parameter I will only have at runtime in Cython.

Here is a minimal example:

from libcpp.algorithm cimport sort
import random

cpdef test_sort():
    cdef int *a = [1,2,3,4]
    cdef int z = random.randrange(5)
    sort(&a[0], &a[3], lambda x,y: abs(x-z) < abs(y-z)) # doesn't compile

I have tried lots of different approaches, but I can't get anything to compile. Is there any way to do this in Cython for native arrays and the libcpp sort function?

Update:

I tried @DavidW's suggestion to write the comparator in C++ but I am getting exactly the same error.

test_sort.pyx:

from libcpp cimport bool
from libcpp.algorithm cimport sort
from libcpp.functional cimport function

import random

cdef extern from "cmp.cpp":
    # I couldn't figure out how to declare the return type for this function
    # I tried function[bool(int, int)] make_cmp(int z) but Cython complained
    make_cmp(int z)

cpdef test_sort():
    cdef int *a = [1,2,3,4]
    cdef int z = random.randrange(5)
    sort(&a[0], &a[3], make_cmp(z))
    print(z)
    for i in range(4):
        print(a[i])

cmp.cpp:

#include <stdlib.h>

std::function<bool(int, int)> make_cmp(int z) {
    return [&z](int x, int y) { return abs(x-z) < abs(y-z); };

setup.py:

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("*.pyx", language="c++")
)

run.py:

from test_sort import test_sort

test_sort()

$ python setup.py build_ext --inplace

Gives the following errors (among other output):

test_sort.cpp: In function ‘PyObject* __pyx_f_9test_sort_test_sort(int)’: test_sort.cpp:1069:33: error: cannot convert ‘std::function’ to ‘PyObject* {aka _object*}’ in assignment __pyx_t_2 = make_cmp(__pyx_v_z); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 13, __pyx_L1_error)

...

test_sort.cpp:1071:78: required from here /usr/include/c++/6/bits/predefined_ops.h:147:11: error: expression cannot be used as a function { return bool(_M_comp(*__it, __val)); }

error: expression cannot be used as a function is exactly what I get if I try to pass the python lambda into sort.

0 Answers
Related