Как я могу написать функцию C, которая принимает int или float?

Я хочу создать функцию в C, которая расширяет Python, который может принимать входы типа float или int. Так что в принципе, я хочу!--0--> и f(5.5) быть приемлемыми входными сигналами.

Я не думаю, что могу использовать if (!PyArg_ParseTuple(args, "i", $value)) потому что он принимает только int или только float.

как я могу сделать так, чтобы моя функция разрешала входы, которые являются интами или поплавками?

мне интересно, должен ли я просто взять вход и поместить его в PyObject и как-то взять тип PyObject - это правильный подход?

3 ответов


если вы объявите функцию C для принятия поплавков, компилятор не будет жаловаться, если вы передадите ему int. Например, эта программа выдает ответ 2.000000:

#include <stdio.h>

float f(float x) {
  return x+1;
}

int main() {
  int i=1;
  printf ("%f", f(i));
}

версия модуля python, iorf.c:

#include <Python.h>

static PyObject *IorFError;

float f(float x) {
  return x+1;
}


static PyObject *
fwrap(PyObject *self, PyObject *args) {
  float in=0.0;
  if (!PyArg_ParseTuple(args, "f", &in))
    return NULL;
  return Py_BuildValue("f", f(in));
}

static PyMethodDef IorFMethods[] = {
    {"fup",  fwrap, METH_VARARGS,
     "Arg + 1"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};


PyMODINIT_FUNC
initiorf(void)
{
  PyObject *m;

  m = Py_InitModule("iorf", IorFMethods);
  if (m == NULL)
    return;

  IorFError = PyErr_NewException("iorf.error", NULL, NULL);
  Py_INCREF(IorFError);
  PyModule_AddObject(m, "error", IorFError);
}

setup.py:

from distutils.core import setup, Extension

module1 = Extension('iorf',
                    sources = ['iorf.c'])

setup (name = 'iorf',
       version = '0.1',
       description = 'This is a test package',
       ext_modules = [module1])

пример:

03:21 $ python
Python 2.7.10 (default, Jul 30 2016, 18:31:42)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import iorf
>>> print iorf.fup(2)
3.0
>>> print iorf.fup(2.5)
3.5

поплавки (обычно) передаются через регистры, в то время как ints (обычно) передаются через стек. Это означает, что вы буквально не можете внутри функции проверить, является ли аргумент float или int.

единственным обходным путем является использование аргументов variadic, причем первый аргумент указывает тип как int или double (не float).

func_int_or_double (uint8_t type, ...) {
va_list ap;
va_start (ap, type);
int intarg;
double doublearg;
if (type==1) {
   intarg = va_arg (ap, int);
}
if (type==2) {
   doublearg = va_arg (ap, double);
}
va_end (ap);
// Your code goes here
}

хотя я не уверен, что python может обрабатывать вызов вариационных функций, поэтому YMMV. Как последняя попытка вы всегда можете sprintf значение в буфер и пусть ваша функция sscanf float/int из буфера.


вы можете проверить тип входного значения, как это:

    PyObject* check_type(PyObject*self, PyObject*args) {
    PyObject*any;

    if (!PyArg_ParseTuple(args, "O", &any)) {
        PyErr_SetString(PyExc_TypeError, "Nope.");
        return NULL;
    }

    if (PyFloat_Check(any)) {
        printf("indeed float");
    }
    else {
        printf("\nint\n");
    }

    Py_INCREF(Py_None);

    return Py_None;
}

вы можете извлечь значение из объекта с помощью:

double result=PyFloat_AsDouble(any);

но в этой конкретной ситуации, вероятно, нет необходимости делать это, независимо от того, что вы разбираете int или float, вы можете захватить его как поплавок и проверить округлость:

    float target;
    if (!PyArg_ParseTuple(args, "f", &target)) {
                PyErr_SetString(PyExc_TypeError, "Nope.");
                return NULL;
    }

    if (target - (int)target) {
        printf("\n input is float \n");
    }
    else {
        printf("\n input is int \n");
    }