How important is it to check return values ​​when using the Python C API?

It seems that every time I call a function that returns PyObject *, I have to add four lines of error checking. Example:

py_fullname = PyObject_CallMethod(os, "path.join", "ss", folder, filename);
if (!py_fullname) {
    Py_DECREF(pygame);
    Py_DECREF(os);
    return NULL;
}
image = PyObject_CallMethodObjArgs(pygame, "image.load", py_fullname, NULL);
Py_DECREF(py_fullname);
if (!image) {
    Py_DECREF(pygame);
    Py_DECREF(os);
    return NULL;
}
image = PyObject_CallMethodObjArgs(image, "convert", NULL);
if (!image) {
    Py_DECREF(pygame);
    Py_DECREF(os);
    return NULL;
}

Am I missing something? Is there a better way to do this? This has the additional problem that I can forget everything that I have to Py_DECREF().

+3
source share
5 answers

goto ( ;-) C ( ++ , ): mainline - errorexit errorexit:, ( , , ), return NULL.

+8

C API. C, , , , ++, ++/Python wrapper.

+2

, , , - , C. deref fullname, , , , , . , , "fullname" :

result = NULL;
py_fullname = PyObject_CallMethod(os, "path.join", "ss", folder, filename);
if (py_fullname) {
    image = PyObject_CallMethodObjArgs(pygame, "image.load", py_fullname, NULL);
    if (image) {
        image = PyObject_CallMethodObjArgs(image, "convert", NULL);
        result = // something to do with image, presumably.
    }
    Py_DECREF(py_fullname);
}
Py_DECREF(pygame);
Py_DECREF(os);
return result;

, , , , (, , , , ), , , , , . , , , .

" ", 5 6 , 5 6 , " ". , , Pythonic, , -)

Goto

result = NULL;
py_fullname = PyObject_CallMethod(os, "path.join", "ss", folder, filename);
if (!py_fullname) goto cleanup_pygame

image = PyObject_CallMethodObjArgs(pygame, "image.load", py_fullname, NULL);
if (!image) goto cleanup_fullname

image = PyObject_CallMethodObjArgs(image, "convert", NULL);
result = // something to do with image, presumably.

cleanup_fullname:
    Py_DECREF(py_fullname);
cleanup_pygame:
    Py_DECREF(pygame);
    Py_DECREF(os);
    return result;

goto , . , (, - , , , , ). , goto , , - , , , , , - :

result = NULL;
helper = allocate_something;
if (!helper) goto return_result;

result = allocate_something_else;
if (!result) goto error_return; // OK, result is already NULL, but it makes the point

result->contents = allocate_another_thing;
if (!result->contents) goto error_cleanup_result;

result->othercontents = allocate_last_thing;
if (!result->othercontents) goto error_cleanup_contents;

free_helper:
    free(helper);
return_result:
    return result;

error_cleanup_contents:
    free(result->contents);
error_cleanup_result:
    free(result);
error_return;
    result = NULL;
    goto free_helper;

, , Python ++ . , . , , , , - , "", , . . , : , , , . -, , , , , .

, . , , , .

+2

, ++ RAII. , ++, , C, , . , - ... , . , Boost.Python.

0

, C ( "goto with tie" ):

result = NULL;
// make sure all variables are initialized

do
{
    py_fullname = PyObject_CallMethod(os, "path.join", "ss", folder, filename);
    if (!py_fullname)
    {
        // some additional error handling here
        // write a trace message with __FILE__ and __LINE__
        break;
    }

    image = PyObject_CallMethodObjArgs(pygame, "image.load", py_fullname, NULL);
    if (!image)
    {
        // some additional error handling here
        break;
    }

    image = PyObject_CallMethodObjArgs(image, "convert", NULL);

    result = // something to do with image, presumably.
} while (true);

if (py_fullname)
    Py_DECREF(py_fullname);
if (pygame)
    Py_DECREF(pygame);
if (os)
    Py_DECREF(os);

return result;

:

  • - ,
  • ifs
  • ( )
  • - , ?

, :

#define CATCH_BEGIN     do {
#define CATCH_END       } while (1!=1);
#define CLEANUP_VOID(function,var) {if (var != NULL) { function(var); var = NULL;}}

:

CLEANUP_VOID(Py_DECREF, py_fullname)
CLEANUP_VOID(Py_DECREF, pygame)
CLEANUP_VOID(Py_DECREF, os)
0

Source: https://habr.com/ru/post/1725341/


All Articles