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?
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
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.
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");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With