Search code examples
pythoncpython-extensions

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?


Solution

  • 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