简体   繁体   中英

How can I convert Python boolean object to C int (or C++ boolean) (Python C API)

I have a variable PyObject that I know is a Python bool. It either is True or False (eg. Py_True or Py_False ). Now I would like to convert it to C++ somehow.

Doing this with strings isn't so hard, there is a helper function for that - PyBytes_AsString that converts python string into C string. Now I need something like that for boolean (or int as there is no bool in C).

Or if there isn't conversion, maybe some function that can compare with true or false? Something like int PyBool_IsTrue(PyObject*) ?

Here is some example code for easier understanding of what I need:

#include <Python.h>

int main()
{
    /* here I create Python boolean with value of True */
    PyObject *b = Py_RETURN_TRUE;
    /* now that I have it I would like to turn in into C type so that I can determine if it's True or False */
    /* something like */
    if (PyBool_IsTrue(b))
    { /* it's true! */ }
    else
    { /* it's false */ }
    return 0;
}

This obviously wouldn't work as there is no such function like PyBool_IsTrue :( how can I do that?

Snippet of Python header (boolobject.h):

/* Boolean object interface */

#ifndef Py_BOOLOBJECT_H
#define Py_BOOLOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif


PyAPI_DATA(PyTypeObject) PyBool_Type;

#define PyBool_Check(x) (Py_TYPE(x) == &PyBool_Type)

/* Py_False and Py_True are the only two bools in existence.
Don't forget to apply Py_INCREF() when returning either!!! */

/* Don't use these directly */
PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct;

/* Use these macros */
#define Py_False ((PyObject *) &_Py_FalseStruct)
#define Py_True ((PyObject *) &_Py_TrueStruct)

/* Macros for returning Py_True or Py_False, respectively */
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False

/* Function to return a bool from a C long */
PyAPI_FUNC(PyObject *) PyBool_FromLong(long);

#ifdef __cplusplus
}
#endif
#endif /* !Py_BOOLOBJECT_H */

Every Python object can have its truthiness evaluated with PyObject_IsTrue , and you should use this in preference to direct PyTrue / PyFalse singleton checking unless you absolutely know, for certain, that the object is a PyBool .

Usage is:

int truthy = PyObject_IsTrue(someobj);
if (truthy == -1) return APPROPRIATEERRORRETURN;

if (truthy)
{ /* it's true! */ }
else
{ /* it's false */ }

You can just test someobj == Py_True if you know it's definitely a bool, or use PyNumber_AsSsize_t to convert any logical integer type (anything implementing __index__ , and bool is a subclass of int , so it's also logically an integer) to a signed size_t value (if __index__ returns a number that doesn't fit in signed size_t , it will return -1 with exception set).

The reason not to do someobj == Py_True in general is because it's like doing if someobj is True: at the Python layer. If someobj is 1 , or an non-empty str , that will treat it as false, when Pythonic code is rarely concerned about being True or False , but rather, "truthiness" and "falsiness".

In addition, this:

PyObject *b = Py_RETURN_TRUE;

is flat wrong. That will incref PyTrue and return it; none of the subsequent code will execute. You'd want:

PyObject *b = Py_True;

for a borrowed reference, adding a subsequent:

Py_INCREF(b);

to make it an owned reference if you intended to return it later (since it's a singleton that won't go away, using a borrowed reference is fine unless you know it will be DECREF ed later, eg because you returned it and passed ownership to a caller who can't know it's a borrowed reference).

The answer is in Python headers but may not be obvious.

Python headers declare 2 somewhat static objects here, with couple of macros:

/* Don't use these directly */
PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct;

/* Use these macros */
#define Py_False ((PyObject *) &_Py_FalseStruct)
#define Py_True ((PyObject *) &_Py_TrueStruct)

/* Macros for returning Py_True or Py_False, respectively */
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False

It seems that both True and False are in fact Python objects and all values in Python that are True or False are in fact references to these two global Py_True and Py_False objects. When such object is returned using Py_RETURN_TRUE , the reference count is incremented.

This means that every C pointer that points to PyObject of value Py_True in fact points to same memory address. Therefore checking if PyObject is true or false is as simple as:

/* here I create Python boolean with value of True */
PyObject *b = Py_True;
Py_INCREF(b);
/* now we compare the address of pointer with address of Py_True */
if (b == Py_True)
{ /* it's true! */ }
else
{ /* it's false */ }

It's generally good idea to use int PyBool_Check(PyObject*) to verify if object in question is Python boolean.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM