Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I write a C function that takes either an int or a float?

I want to create a function in C that extends Python that can take inputs of either float or int type. So basically, I want f(5) and f(5.5) to be acceptable inputs.

I don't think I can use if (!PyArg_ParseTuple(args, "i", $value)) because it only takes only int or only float.

How can I make my function allow inputs that are either ints or floats?

I'm wondering if I should just take the input and put it into a PyObject and somehow take the type of the PyObject - is that the right approach?

like image 361
ansg191 Avatar asked Oct 04 '16 04:10

ansg191


3 Answers

If you declare a C function to accept floats, the compiler won't complain if you hand it an int. For instance, this program produces the answer 2.000000:

#include <stdio.h>

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

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

A python module version, 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);
}

The 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])

An example:

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
like image 75
Hack Saw Avatar answered Oct 20 '22 14:10

Hack Saw


Floats are (usually) passed in via registers while ints are (usually) passed in via the stack. This means that you literally cannot, inside the function, check whether the argument is a float or an int.

The only workaround is to use variadic arguments, with the first argument specifying the type as either int or double (not 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
}

Although, I'm not really sure if python can handle calling variadic functions, so YMMV. As a last ditch-effort you can always sprintf the value into a buffer and let your function sscanf float/int from the buffer.

like image 1
Lelanthran Avatar answered Oct 20 '22 12:10

Lelanthran


You can check type of input value like this:

    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;
}

You can extract float value from object using:

double result=PyFloat_AsDouble(any);

But in this particular situation probably no need to do this, no matter what you parsing int or float you can grab it as a float and check on roundness:

    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");
    }
like image 1
Anatoly Strashkevich Avatar answered Oct 20 '22 14:10

Anatoly Strashkevich