Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rewrite GCC cleanup macro with nested function for Clang?

I'm trying to work through an issue on a third party library. The issue is the library uses GCC's nested functions buried in a macro, and Clang does not support nested functions and has no plans to do so (cf., Clang Bug 6378 - error: illegal storage class on function).

Here's the macro that's the pain point for me and Clang:

#define RAII_VAR(vartype, varname, initval, dtor) \
    /* Prototype needed due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36774 */ \
    auto void _dtor_ ## varname (vartype * v); \
    void _dtor_ ## varname (vartype * v) { dtor(*v); } \
    vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)

And here's how its used (from the code comments):

 * void do_stuff(const char *name)
 * {
 *     RAII_VAR(struct mything *, thing, find_mything(name), ao2_cleanup);
 *     if (!thing) {
 *         return;
 *     }
 *     if (error) {
 *         return;
 *     }
 *     do_stuff_with_thing(thing);
 * }

The Clang User Manual states to use C++ and a lambda function to emulate. I'm not sure that's the best strategy, and a C project will likely not accept a C++ patch (they would probably tar and feather me first).

Is there a way to rewrite the macro so that's its (1) more accommodating to Clang, and (2) preserves original function semantics?

like image 247
jww Avatar asked Jul 25 '14 15:07

jww


2 Answers

Clang doesn't support GCC nested functions, but it does support Objective C-style "blocks", even in C mode:

void f(void * d) {
    void (^g)(void *) = ^(void * d){ };
    g(d);
}

You need to invoke it with the clang command rather than gcc, and also (?) pass -fblocks -lBlocksRuntime to the compiler.

You can't use a block as a cleanup value directly, since it has to be a function name, so (stealing ideas from here) you need to add a layer of indirection. Define a single function to clean up void blocks, and make your RAII'd variable the block that you want to run at the end of the scope:

typedef void (^cleanup_block)(void);
static inline void do_cleanup(cleanup_block * b) { (*b)(); }

void do_stuff(const char *name) {
    cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ };
}

Because blocks form closures, you can then place the operations on your variables to cleanup directly inside that block...

void do_stuff(const char *name) {
    struct mything * thing;
    cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ ao2_cleanup(thing); };
}

...and that should run at the end of the scope as before, being invoked by the cleanup on the block. Rearrange the macro and add a __LINE__ so it works with multiple declarations:

#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A##B

#define RAII_VAR(vartype, varname, initval, dtor) \
    vartype varname = (initval); \
    cleanup_block __attribute__((cleanup(do_cleanup))) CAT(__b_, __LINE__) = ^{ dtor(varname); };

void do_stuff(const char *name) {
    RAII_VAR(struct mything *, thing, NULL, ao2_cleanup);
    ...

Something like that, anyway.

like image 196
Leushenko Avatar answered Nov 14 '22 21:11

Leushenko


I believe you can do this without using a clang-specific version, I'd try something like this (untested, may require a few extra casts):

struct __destructor_data {
    void (*func)(void *);
    void **data;
}

static inline __destructor(struct __destructor_data *data)
{
    data->func(*data->data);
}

#define RAII_VAR(vartype, varname, initval, dtor)  \
    vartype varname = initval;                     \
    __attribute((cleanup(__destructor)))           \
        struct __destructor_data __dd ## varname = \
             { dtor, &varname };

In our project we have a gcc-specific _auto_(dtor) macro that precedes the normal variable declaration, e.g.:

_auto_(free) char *str = strdup("hello");

In this case our macro can't add anything after the variable declaration and also doesn't know the name of the variable, so to avoid using gcc-specific nested functions I came up with the following hackish version in case this helps anyone:

static void *__autodestruct_value = NULL;
static void (*__autodestruct_dtor)(void *) = NULL;

static inline void __autodestruct_save_dtor(void **dtor)
{
       __autodestruct_dtor = *dtor;
       __autodestruct_dtor(__autodestruct_value);
}

static inline void __autodestruct_save_value(void *data)
{
       __autodestruct_value = *(void **) data;
}

#define __AUTODESTRUCT(var, func)                              \
       __attribute((cleanup(__autodestruct_save_dtor)))      \
               void *__dtor ## var = (void (*)(void *))(func); \
       __attribute((cleanup(__autodestruct_save_value)))
 
#define _AUTODESTRUCT(var, func)                       \
       __AUTODESTRUCT(var, func)

#define _auto_(func)                                    \
        _AUTODESTRUCT(__COUNTER__, func)

This is hackish because it depends on the order the destructors are called by the compiler being the reverse of the order of the declarations, and it has a few obvious downsides compared to the gcc-specific version but it works with both compilers.

like image 22
balrog Avatar answered Nov 14 '22 21:11

balrog