• Re: Structured Programming (was Re: ?The end of software secrecy is alm

    From Lawrence D?Oliveiro@3:633/10 to All on Sun Nov 1 23:38:32 2026
    Subject: Re: Structured Programming (was Re: ?The end of software secrecy is almost upon us?)

    Anyway, to get the discussion back on track:

    I put together an example here <https://gitlab.com/ldo/a_structured_discipline_of_programming>. This
    is an extension module that offers services to Python code, because
    that is just about the worst example I could think of, for those who
    think gotos can cut it.

    Problems I am trying to address:

    * Leaking memory: allocating something and forgetting to dispose of
    it
    * Double-free: the opposite problem, trying to free a memory block
    that has already been freed

    The basic structured-programming pattern I follow is:

    1: Initialize a pointer variable to `NULL`
    2: Allocate memory for the pointer (this might fail)
    3: Correctly free the pointer memory, regardless of allocation
    success/failure

    This project uses the implementation of a Python extension module as a
    specific example, since Python itself is a highly dynamic language,
    and so code that is written in a lower-level-language like C for
    interfacing to Python typically has to deal a lot with allocation and management of Python objects and other dynamic memory.

    In more concrete terms, the code pattern looks like this:

    PyObject * obj = NULL; /* step 1 */
    do /*once*/
    {
    ... possible other stuff ...
    allocate obj; /* step 2 */
    if (PyErr_Occurred())
    break;
    ... possible other stuff using obj ...
    }
    while (false);
    Py_XDECREF(obj); /* step 3 */

    Assuming there are no `goto`s in the ?...? parts (or `return`
    statements, or shenanigans like `setjmp`(3)/`longjmp`(3)), it should
    be clear that, regardless of what happens in those parts (provided
    they don?t free or reallocate `obj`), any memory allocated to `obj`
    will be correctly freed.

    Key to this pattern is the fact that `Py_XDECREF` is a noop (and
    raises no error) if it is passed a `NULL` pointer. This is also the
    semantics of the `free`(3) routine in the C standard library, and I
    recommend that any memory-disposal routine you create implement the
    same behaviour. It saves checking for unallocated storage in the
    cleanup code: just unconditionally dispose of everything temporary
    that might have been allocated.

    The above basic structure scales easily. For example, coping with
    multiple objects needing allocating:

    PyObject * obj1 = NULL;
    PyObject * obj2 = NULL;
    do /*once*/
    {
    ... possible other stuff ...
    allocate obj1;
    if (PyErr_Occurred())
    break;
    ... possible other stuff using obj1 ...
    allocate obj2;
    if (PyErr_Occurred())
    break;
    ... possible other stuff using obj1 and obj2 ...
    }
    while (false);
    Py_XDECREF(obj1);
    Py_XDECREF(obj2);

    Nested blocks:

    PyObject * obj1 = NULL;
    do /*once*/
    {
    ... possible other stuff ...
    allocate obj1;
    if (PyErr_Occurred())
    break;
    ... possible other stuff using obj1 ...
    {
    PyObject * obj2 = NULL;
    do /*once*/
    {
    ... even more possible stuff using obj1 ...
    allocate obj2;
    if (PyErr_Occurred())
    break;
    ... possible other stuff using obj1 and obj2 ...
    }
    while (false);
    Py_XDECREF(obj2);
    }
    if (PyErr_Occurred())
    break;
    ... yet more possible stuff using obj1 ...
    }
    while (false);
    Py_XDECREF(obj1);

    Allocation within loops:

    for (ssize_t i = 0;;)
    {
    if (i == nr_items)
    break;
    PyObject * const item = PyTuple_GetItem(items, i);
    if (item == NULL)
    break;
    {
    PyObject * key = NULL;
    PyObject * value = NULL;
    do /*once*/
    {
    ... construct key ...
    if (PyErr_Occurred())
    break;
    ... construct value ...
    if (PyErr_Occurred())
    break;
    if (PyDict_SetItem(tempresult, key, value) < 0)
    break;
    }
    while (false);
    Py_XDECREF(key);
    Py_XDECREF(value);
    }
    if (PyErr_Occurred())
    break;
    ++i;
    } /*for*/
    if (PyErr_Occurred())
    break;

    Etc, etc.

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)