用Clang的嵌套函数重写GCC清理宏?

jww*_*jww 5 c macros gcc clang gcc-extensions

我正在尝试解决第三方库中的问题.问题是库使用GCC嵌套在宏中的嵌套函数,而Clang不支持嵌套函数,并且没有计划这样做(参见,Clang Bug 6378 - 错误:函数上的非法存储类).

这是我和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)
Run Code Online (Sandbox Code Playgroud)

以下是它的使用方法(来自代码注释):

 * 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);
 * }
Run Code Online (Sandbox Code Playgroud)

所述锵用户Manua升状态中使用C++和lambda函数来模拟.我不确定这是最好的策略,而且C项目可能不会接受C++补丁(他们可能会首先考虑并且让我感到厌烦).

有没有办法重写宏,以便它(1)更适应Clang,(2)保留原始函数语义?

Leu*_*nko 6

Clang不支持GCC嵌套函数,但它确实支持Objective C风格的"块",即使在C模式下:

void f(void * d) {
    void (^g)(void *) = ^(void * d){ };
    g(d);
}
Run Code Online (Sandbox Code Playgroud)

您需要使用clang命令调用它而不是gcc,并且(?)传递-fblocks -lBlocksRuntime给编译器.

你不能直接使用块作为cleanup值,因为它必须是一个函数名,所以(从这里窃取想法)你需要添加一个间接层.定义单个函数来清理void块,并使RAII'd变量成为您想要在作用域末尾运行的:

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 = ^{ };
}
Run Code Online (Sandbox Code Playgroud)

因为块形成闭包,所以您可以将操作放在变量上以直接在块内清理...

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

......并且应该像以前一样在范围的末尾运行,由块上的清理调用.重新排列宏并添加一个__LINE__,使其适用于多个声明:

#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);
    ...
Run Code Online (Sandbox Code Playgroud)

无论如何,这样的事情.