2009.11.29 - last edit >> Sun, 29 Nov 2009 13:55:22 -0500

When Standard Commenting Isn't Enough

I modified a matrix to handle (and reuse) its own memory. For example, when a matrix is created to be a subsection of another one, the submatrix can use its super's matrix elements. (super/sub =~ parent/child)

But there are some interesting dynamics at play. The submatrix may persist (case 2) longer than its super, and should be responsible (case 4) for cleaning up the memory, allowing the programmer to free up variable count and write less cluttered code. Likewise, if there's a chain of the super/sub relation and a middle one becomes useless (case 1), it can free the memory for its "row" variable (of type number**, an array of pointers to beginnings of rows). If there are no such links (case 3), all memory can go! (elements, rows, and its own malloc'd space).

In case 2, the super's row data cannot be freed, since it also points to the head of the element memory, whose location must clearly be known in order to be freed.

That can get confusing... and for the system to be implemented correctly, the programmer must have a strong understanding of the essential routines. A standard commenting scheme just wouldn't cut it this time...

void cleanup_matrix (struct matrix* A)
{
    assert (A);
    assert (A->refc > 0);

    -- A->refc;
    if (mrefc_matrix (A)) return;

        /* When I get older, */
    if (  A->super &&   A->subc)
    {
            /* Losin' my rows, */
        free_rows_matrix (A);

            /* Many cycles through */
        return;
    }

        /* Will you still be visiting my indices, */
    if (! A->super &&   A->subc)
            /* When I can reach malloc'd memory? */
        return;

        /* If I am left without any links, */
    if (! A->super && ! A->subc)
    {
        free_elems_matrix (A);
        free_rows_matrix (A);
        free_matrix (A);
        return;
    }
        /* Would I ever know? */

    if (  A->super && ! A->subc)
    {
            /* Will you still need me, */
        A->super->subc -= 1;

        if (! A->super->subc)
                /* Will you still free me, */
            cleanup_matrix (A->super);

        free_rows_matrix (A);
        free_matrix (A);
        return;
    }
        /* When I'm out of scope? */
}


For those interested, the memory isn't always freed - the matrix struct has booleans freep (not on stack) and deepfreep (row/element data should be freed, they will not be reused). There is also a constp data member, for when the matrix elements should not be modified. This is useful for the programmer to specify if the result of, say, a Gaussian elimination, is needed or if the initial matrix state will be used as well.

-- Alex