Search code examples
cgccmacrosclanggcc-extensions

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?


Solution

  • 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.