23 December 2010

getopt.h:195: error: redefinition of 'struct option'

While working with Gnulib recently I kept running into problems with its getopt-gnu module:

In file included from ../../esio/gnulib/argp.h:24:0,
                 from ../../esio/apps/esio_bench.c:7:
../gnulib/getopt.h:195:8: error: redefinition of ‘struct option’
/usr/include/getopt.h:106:8: note: originally defined here
../gnulib/getopt.h:242:12: error: conflicting types for ‘getopt_long’
/usr/include/getopt.h:175:12: note: previous declaration of ‘getopt_long’ was here
../gnulib/getopt.h:246:12: error: conflicting types for ‘getopt_long_only’
/usr/include/getopt.h:179:12: note: previous declaration of ‘getopt_long_only’ was here
make[2]: *** [esio_bench.o] Error 1
which looked suspiciously like someone else's problem but couldn't be resolved by a simple make distclean.

Turns out that I failed to include the required

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

at the top of my source file. Oops. This detail is obvious in hindsight but slightly difficult to dig out of the relevant section of the gnulib manual.

16 December 2010

snprintf + realloc = snprintf_realloc

Recently, in C99, I needed to format some arbitrary-length string content using snprintf. I could re-use the same buffer because I only needed one result at a time. I wanted to (hopefully) minimize the number of malloc calls necessary. Finally, I wanted to (mostly) maintain snprintf's interface including its error semantics.

My use case looked like this...

    char *buf = NULL;
    size_t len = 0;
    while (/* more work */) {
        if (0 > snprintf_realloc(&buf, &len, /*format specifier*/, ...)) {
            /* error occurred, report it, break */
        }
        /* use buf to perform work */
    }
    if (buf) free(buf);

Here is the snprintf_realloc method I created including the relevant Doxygen and FCTX-based unit tests. The source below should build and run provided that fct.h is also in the same directory:

#include <stdarg.h>
#include <stdlib.h>

#include "fct.h"

/**
 * Call snprintf(*str, *size, format, ...) and reallocate the buffer
 * pointed to by *str as appropriate to contain the entire result.  On
 * exit, *str and *size will contain a pointer to the
 * realloced buffer and its maximum usable size, respectively.
 *
 * The reallocation scheme attempts to reduce the reallocation calls when the
 * same str and size arguments are used repeatedly.  It is
 * valid to pass *str == NULL and *size == 0 and then have
 * the buffer allocated to perfectly fit the result.
 *
 * @param[in,out] str    Pointer to the buffer in which to write the result.
 * @param[in,out] size   Pointer to the initial buffer size.
 * @param[in]     format Format specifier to use in sprintf call.
 * @param[in]     ...    Variable number of arguments corresponding
 *                       to \c format.
 *
 * @return On success, the number of characters (not including the trailing
 *         '\0') written to *str.  On error, a negative value
 *         is returned, *str is freed and *size
 *         is set to zero.
 */
int snprintf_realloc(char **str, size_t *size, const char *format, ...);

int
snprintf_realloc(char **str, size_t *size, const char *format, ...)
{
    int retval, needed;
    va_list ap;
    va_start(ap, format);
    while (   0     <= (retval = vsnprintf(*str, *size, format, ap)) // Error?
           && *size <  (needed = retval + 1)) {                      // Space?
        va_end(ap);
        *size *= 2;                                                  // Space?
        if (*size < needed) *size = needed;                          // Space!
        char *p = realloc(*str, *size);                              // Alloc
        if (p) {
            *str = p;
        } else {
            free(*str);
            *str  = NULL;
            *size = 0;
            return -1;
        }
        va_start(ap, format);
    }
    va_end(ap);
    return retval;
}

FCT_BGN()
{
    FCT_FIXTURE_SUITE_BGN(snprintf_realloc)
    {
        const char s[] = "1234";
        char *ptr;
        size_t size;

        FCT_SETUP_BGN()
        {
            ptr  = NULL;
            size = 0;
        }
        FCT_SETUP_END();

        FCT_TEARDOWN_BGN()
        {
            if (ptr) free(ptr);
        }
        FCT_TEARDOWN_END();

        FCT_TEST_BGN(snprintf_realloc)
        {
            // Initial should cause malloc-like behavior
            fct_chk_eq_int(4, snprintf_realloc(&ptr, &size, "%s", s));
            fct_chk(ptr);
            fct_chk_eq_int(size, 5);
            fct_chk_eq_str(ptr, s);

            // Repeated size should not cause any new buffer allocation
            {
                char *last_ptr = ptr;
                fct_chk_eq_int(4, snprintf_realloc(&ptr, &size, "%s", s));
                fct_chk(last_ptr == ptr);
                fct_chk_eq_int(size, 5);
                fct_chk_eq_str(ptr, s);
            }

            // Request requiring more than twice the space should
            // realloc memory to fit exactly.
            fct_chk_eq_int(12, snprintf_realloc(&ptr, &size, "%s%s%s",
                                                s, s, s));
            fct_chk_eq_int(size, 13);
            fct_chk_eq_str(ptr, "123412341234");

            // Request requiring less than twice the space should
            // cause a doubling of the buffer size.
            fct_chk_eq_int(16, snprintf_realloc(&ptr, &size, "%s%s%s%s",
                                                s, s, s, s));
            fct_chk_eq_int(size, 26);
            fct_chk_eq_str(ptr, "1234123412341234");
        }
        FCT_TEST_END();
    }
    FCT_FIXTURE_SUITE_END();
}
FCT_END()

Valgrind, after suppressing some unrelated FCTX warnings, gives this test a clean bill of health. If anyone's interested, aside from passing NULL pointers in or relying upon undefined snprintf behavior, I'd love to hear of ways to break this particular snippet.

04 December 2010

APS DFD 2010 Talk Slides

As promised, sort of, here are the slides from my APS DFD 2010 talk:

A WENO-Based Code for Investigating RANS Model Closures for Multicomponent Hydrodynamic Instabilities

Go listen to The Mikie Show

An old acquaintance has been running an amazing radio show called The Mikie Show for some time now. If you get the chance, take a listen. Think Mister Rogers on the radio if Mister Rogers happened to be a sound designer/musician with a groanful sense of humor.

Recently my friend Billy, an arborist, was in episode 25. Billy's fun. When asked "Does hugging a tree really help it?" Billy didn't miss a beat and replied "It depends what you're going to do next."

Subscribe Subscribe to The Return of Agent Zlerich