我正在尝试解决第三方库中的问题。问题是库使用GCC的嵌套函数隐藏在宏中,而Clang不支持嵌套函数,也没有计划这样做(参见,Clang Bug 6378 - error: illegal storage class on function)。
这就是我和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)
以下是它的使用方法(来自代码注释):
* 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);
* }
Clang User Manua l声明使用C ++和lambda函数进行模拟。我不确定这是最好的策略,而C项目很可能不接受C ++补丁(他们可能会首先考虑并惹我生气)。
有没有办法重写宏以使其(1)更适应Clang,以及(2)保留原始函数语义?
答案 0 :(得分:6)
Clang不支持GCC嵌套函数,但它支持Objective C-style "blocks",即使在C模式下也是如此:
void f(void * d) {
void (^g)(void *) = ^(void * d){ };
g(d);
}
您需要使用clang
命令而不是gcc
调用它,并且还(?)将-fblocks -lBlocksRuntime
传递给编译器。
您不能直接将块用作cleanup
值,因为它必须是函数名称,因此(从here窃取想法)您需要添加一层间接。定义一个函数来清理void块,并使你想要在范围结束时运行的块的RAII变量:
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 = ^{ };
}
因为块形成闭包,所以你可以将操作放在你的变量上直接清理那个块......
void do_stuff(const char *name) {
struct mything * thing;
cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ ao2_cleanup(thing); };
}
...并且应该像以前一样在范围的末尾运行,由块上的清理调用。重新排列宏并添加__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);
...
无论如何都是这样的。
答案 1 :(得分:1)
我相信你可以在不使用 clang 特定版本的情况下做到这一点,我会尝试这样的事情(未经测试,可能需要一些额外的演员):
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 };
在我们的项目中,我们在普通变量声明之前有一个特定于 gcc 的 _auto_(dtor)
宏,例如:
_auto_(free) char *str = strdup("hello");
在这种情况下,我们的宏不能在变量声明之后添加任何,也不知道变量的名称,因此为了避免使用特定于 gcc 的嵌套函数,我想出了如果这对任何人有帮助,请遵循 hackish 版本:
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)
这是一种hackish,因为它取决于编译器调用析构函数的顺序与声明的顺序相反,并且与特定于 gcc 的版本相比,它有一些明显的缺点,但它适用于两种编译器。