-1

I am trying to develop a C module for applying a selection sort algorithm to a list of data and returning the sorted list back to the Python program as a Python list format.

However, I found that the result of the program below will be something like [431863472]

#include <Python.h>

int sorting(int n[])
{
    for (int i=0; i < sizeof(n)-1; i++){
        int minpos = i;
        for (int j=0; j < sizeof(n)-1; j++){
            if (n[j] < n[minpos]){
                minpos = j;
            }
        int temp = n[i];
        n[i] = n[minpos];
        n[minpos] = temp;  
        }
    }
    return n;
}

static PyObject* selectSort(PyObject* self, PyObject* args)
{
    // instantiate our `n` value
    int n[20000];
    // if our `n` value
    if(!PyArg_ParseTuple(args, "O", &n))
        return NULL;
    return Py_BuildValue("[i]", sorting(n));
}

static PyMethodDef myMethods[] = {
    { "selectSort", selectSort, METH_VARARGS, "Applying selection sort" },
    { NULL, NULL, 0, NULL }
};

static struct PyModuleDef myModule = {
    PyModuleDef_HEAD_INIT,
    "myModule",
    "Test Module",
    -1,
    myMethods
};

PyMODINIT_FUNC PyInit_myModule(void)
{
    return PyModule_Create(&myModule);
}

Is it the problem of passing the array to the python? Or I should not use "O" or "[i]"?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • 1
    What you have shown doesn't produce any output by itself. Surely you are calling this code from Python somehow? Please show a [mre] of it. – mkrieger1 Dec 21 '21 at 20:56
  • I *believe* you’ll need `[iiii]` (an `i` for each element in the list) in the return statement. Perhaps [this answer](https://stackoverflow.com/a/50683462/6340496) will be of assistance. – S3DEV Dec 21 '21 at 21:20
  • `int sorting(int n[])` returns a single integer so `[i]` is correct (but not the desired behaviour of course) – DavidW Dec 21 '21 at 21:33
  • @DavidW - The declaration certainly states that `sorting` returns a single `int`, however `n` is initialised to an array size of 20000, and `n` is indexed all throughout the function. It appears the function is designed to return the array (although not possible in C). Unless the pointer is being returned? – S3DEV Dec 21 '21 at 21:39
  • Does `sizeof(n)` really work when you have declared `n` as an array without a given length (`int n[]`)? – md2perpe Dec 21 '21 at 21:48
  • The function `sorting` returns the address of the array `n`, so that's what `selectSort` returns as a Python `int`. – md2perpe Dec 21 '21 at 22:02
  • @S3DEV. On [CPP Insights](https://cppinsights.io/s/ce2bd7c7) it gives a warning: "warning: sizeof on array function parameter will return size of 'int *' instead of 'int []'". How could it even work? Would there be different copies of the function when arrays of different sizes are passed in different calls? – md2perpe Dec 21 '21 at 22:10
  • `Py_BuildValue("[i]", ...)` will build a list with only one value. Use `PyList_New` and `PyList_SetItem` to build your list when the number of elements is unknown. See https://stackoverflow.com/a/50683462/1412534 – md2perpe Dec 21 '21 at 22:15
  • When you use `PyArg_ParseTuple(args, "O", &n)` the value of `n` will be a pointer to a Python object, not a C array. You need to parse this object (which should be a Python list, I suppose) into a C array. – md2perpe Dec 21 '21 at 22:17
  • 1
    There are so many errors in this code. Don't you get a bunch of warnings when you compile it? – md2perpe Dec 21 '21 at 22:18
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Dec 29 '21 at 13:17

1 Answers1

1

Here's one possible solution. Note how PyArg_ParseTuple makes argument contain a link to a Python object. Then I check that this actually is a list. The values are converted from Python integer objects to integers and copied to a local array, which is passed to select_sort. After sorting a new Python list is created with the result, and this is returned.

The solution is not perfect. For example, I do not check that all elements of the list are integers.

#include <Python.h>

void select_sort(int count, int *values)
{
    for (int i = 0; i < count; i++)
    {
        int minpos = i;
        for (int j = 0; j < count; j++)
        {
            if (values[j] < values[minpos])
            {
                minpos = j;
            }

            int temp = values[i];
            values[i] = values[minpos];
            values[minpos] = temp;
        }
    }
}

#define MAX_LENGTH 20000

static PyObject *py_select_sort(PyObject *self, PyObject *args)
{
    PyObject *argument;

    // Get first object from tuple
    if (!PyArg_ParseTuple(args, "O", &argument))
    {
        return NULL;
    }

    // Is it a list?
    if (!PyList_Check(argument))
    {
        PyErr_SetString(PyExc_TypeError, "argument should be a list");
        return NULL;
    }

    // For simplicity we have a maximum size
    int length = PyList_Size(argument);
    if (length > MAX_LENGTH)
    {
        PyErr_SetString(PyExc_ValueError, "list is too big");
        return NULL;
    }

    // Local array to work with
    int values[MAX_LENGTH];

    // Copy values from Python list to local array
    for (int i = 0; i < length; i++)
    {
        values[i] = PyLong_AsLong(
            PyList_GetItem(argument, i)
        );
    }

    // Now sort!
    select_sort(length, values);

    // We'll return a new list
    PyObject *result = PyList_New(length);

    // Copy values back from local array to new Python list
    for (int i = 0; i < length; i++)
    {
        PyObject *value = PyLong_FromLong(values[i]);
        Py_IncRef(value);
        PyList_SetItem(result, i, value);
    }

    return result;
}

static PyMethodDef myMethods[] = {
    {"selectSort", py_select_sort, METH_VARARGS, "Applying selection sort"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef myModule = {
    PyModuleDef_HEAD_INIT,
    "myModule",
    "Test Module",
    -1,
    myMethods
};

PyMODINIT_FUNC PyInit_myModule(void)
{
    return PyModule_Create(&myModule);
}
md2perpe
  • 3,372
  • 2
  • 18
  • 22