Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building a C iterator macro with a pre-C99 compiler

My code, which must be compiled with a pre-C99 compiler (we're working on updating but it's an enormous task), is calling into a utility library designed with C99 in mind. In particular, these utilities define a hashmap type and provide a macro to iterate through it similar to the following:

#define MAP_FOREACH(key, val, map) \
    for (struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup))); \
        (key) = iter->pair->key, \
        (value) = iter->pair->value; \
        iter = iter->get_next_cb())

The actual code has a bit more to it (functionality to make sure the iterator's name is unique, etc.), but this covers the meat of my problem which is that pre-1999 versions of C don't support initializing variables inside a for loop. Now the obvious fix here would be to move the initialization outside of the loop, with code that looks like:

// Doesn't work
#define MAP_FOREACH(key, val, map) \
    struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup)));
    for (; \
        (key) = iter->pair->key, \
        (value) = iter->pair->value; \
        iter = iter->get_next_cb())

The issue is that __attribute__((cleanup(_map_iter_cleanup))) bit. According to the GCC documentation, that defines cleanup code attached to the variable, which gets run when the variable goes out of scope. Because we've moved the iterator declaration outside the for loop, its scope has changed, and the cleanup code gets run elsewhere. Unfortunately, other parts of the library rely on the iterator getting cleaned up immediately - the map keeps track of how many iterators have been initiated, and throws an error if it's destroyed before they're all gone.

I've been trying and failing to think of a clean way around this for a few days now, but I'm coming up short. I don't really want to re-implement the macro-plus-cleanup in the pre-C99 code, but this library is used all over the place and changing the API to include a post-iteration cleanup call would be painful, not to mention inelegant.

Has anyone encountered this sort of thing before and know a way around it?

EDIT: we're using GCC 4.2.2 with the -std=c89 option

like image 437
Dan Avatar asked Dec 12 '25 11:12

Dan


1 Answers

I am not fully aware of the missing functionalities of pre-C99 standard, but I think you could do it like the following:

#define MAP_FOREACH(key, val, map) \
    {
        struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup)));
        for (; \
            (key) = iter->pair->key, \
            (value) = iter->pair->value; \
            iter = iter->get_next_cb())

And then wherever you use it, you'd have to append the loop with an extra closing curly bracket }.

like image 109
Utkan Gezer Avatar answered Dec 15 '25 19:12

Utkan Gezer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!