Python C api - перегрузка функций - PullRequest
2 голосов
/ 08 мая 2019

У меня есть ряд функций C, которые принимают разные аргументы, например,

foo_i(int a)

foo_c(char c)

Возможно ли перегрузить эти функции в Python C API?

Я пытался использовать следующую таблицу методов:

static PyMethodDef test_methods[] = {
    {"foo", (PyCFunction)foo_i, METH_VARARGS, "int"},
    {"foo", (PyCFunction)foo_c, METH_VARARGS, "char"},
    {NULL, NULL, 0, NULL}
};

Но когда я вызываю foo из python, я всегда заканчиваю тем, что использую функцию внизу таблицы.

Любые идеи о том, как вызвать оба foo_i () и foo_c () , используя foo () в Python C-api?

Спасибо!

1 Ответ

2 голосов
/ 08 мая 2019

Либо дайте им разные имена уровня Python, либо напишите одну функцию-обертку, которая проверяет предоставленный аргумент и отправляет правильную «реальную» функцию.Сам Python не имеет прямой поддержки перегрузки функций, основанных на типах аргументов.

Если вы хотите, чтобы обертка была написана для вас, вы можете взглянуть на pybind11, что позволяет перегрузку в том смысле, в котором вы пытаетесь(он делает это через оболочку проверки типа под капотом, так что это просто синтаксический сахар, а не изменение поведения).

Не проверенный пример кода:

static PyObject*
foo_wrapper(PyObject *self, PyObject *arg)
{
    Py_buffer view;
    Py_ssize_t ival;

    // Check for/handle length 1 bytes-like object (bytes, bytearray, small mmap, etc.)
    if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) == 0) {
        if (view.len != 1) {
             PyErr_Format(PyExc_ValueError, "Must receive exactly one byte, got %zd", view.len);
             PyBuffer_Release(&view);
             return NULL;
        }
        foo_c(((char*)view.buf)[0]);
        Py_RETURN_NONE; // Or convert return from foo_c if it exists
    }

    // Check for/handle integer-like object that fits in C int
    PyErr_Clear(); // Ignore error for objects not supporting buffer protocol
    ival = PyNumber_AsSsize_t(arg, PyExc_ValueError);
    if (PyErr_Occurred()) {
        if (PyErr_ExceptionMatches(PyExc_TypeError)) {
            // Replace with general error message about both accepted argument types,
            // since only reporting error from int conversion might confuse folks
            PyErr_Format(PyExc_TypeError, "Argument must be length 1 bytes-like object or integer; received %R", Py_TYPE(arg));
        }
        return NULL;
    }

    // Check valid range (Py_ssize_t often larger than int)
    if (ival < INT_MIN or ival > INT_MAX) {
        return PyErr_Format(PyExc_ValueError, "Integer must be in range [%d-%d]; received %zd", INT_MIN, INT_MAX, ival);
    }

    foo_i((int)ival);
    Py_RETURN_NONE; // Or convert return from foo_i if it exists
}

static PyMethodDef test_methods[] = {
    {"foo", (PyCFunction)foo_wrapper, METH_O, "Wrapper for foo_c and foo_i"},
    {NULL, NULL, 0, NULL}
};
...