What is the "correct" way to pass a boolean to a Python C extension?
Asked Answered
U

3

29

This is a simple example from the python documentation (http://docs.python.org/extending/extending.html):

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}

If I want to pass an additional boolean parameter to the function - what's the "correct" way to do it?

There doesn't seem to be a bool option to pass to PyArg_ParseTuple(). So I thought of the following:

  1. read an integer and just use the value (as bool is a subclass of int)
  2. call PyBool_FromLong() on an integer
  3. read and object and call PyBool_Check() to verify it is a bool
  4. maybe there's a way to get any type of variable and get its truth value (i.e an empty array will is falsy etc.) which is what python function usually do.

Any of these preferable? Other options?

Unmake answered 16/2, 2012 at 17:37 Comment(1)
A completion of the topic 'bool' for Python/C is Py_RETURN_TRUE and Py_RETURN_FALSE where you want to return a bool from C/C++. The macros should be used to increment references.Creature
A
27

4 maybe there's a way to get any type of variable and get its truth value (i.e an empty array will is falsy etc.) which is what python function usually do.

Yes: (from Python/C API Reference)

int PyObject_IsTrue(PyObject *o)

Returns 1 if the object o is considered to be true, and 0 otherwise. This is equivalent to the Python expression not not o. On failure, return -1.

EDIT. To answer the actual question, I think approach 1 is correct, because int really is the corresponding type in C. Approach 4 is good, but if you document your function as taking a bool, you are not obligated to accept just any object. Explicit type checks as in 3 without a reason are frowned upon in Python. Conversion to another Python object as in 2 does not help your C code.

Ares answered 16/2, 2012 at 17:52 Comment(0)
B
21

Currently, parsing an integer (as "i") is the accepted way to take a bool.

From Python 3.3, PyArg_ParseTuple will accept "p" (for "predicate"), per the latest NEWS:

  • Issue #14705: The PyArg_Parse() family of functions now support the 'p' format unit, which accepts a "boolean predicate" argument. It converts any Python value into an integer--0 if it is "false", and 1 otherwise.

Note that when using PyArg_ParseTuple with "p", the argument must be a (pointer to) int, not the C99 bool type:

int x;    // not "bool x"
PyArg_ParseTuple(args, "p", &x);
Beside answered 22/5, 2012 at 11:1 Comment(4)
I've been using this and found that in C I must declare variables as int (not bool). Or, when using multiple bools, they will not get set correctly (possibly an issue with the difference in sizes between a bool and an int).Vaishnava
@Vaishnava It states that it uses an integer in the response here.Ravish
Yes it does, but often in C you can use int and bool interchangeably, in this case you cannot. I can remove my comment if it is superflous.Vaishnava
@Vaishnava sorry, I missed your original comment. That's an important point and I've updated my answer. Thanks!Beside
W
7

I have found another approach:

PyObject* py_expectArgs;
bool expectArgs;

PyArg_ParseTupleAndKeywords(args, keywds, (char *)"O!", (char **)kwlist, &PyBool_Type, &py_expectArgs);

expectArgs = PyObject_IsTrue(py_expectArgs);

In case of wrong param call there is "auto" exception "argument 1 must be bool, not int"

Wellknown answered 16/9, 2013 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.