使用GCC和clang __attribute __((cleanup))和指针声明的一种好的惯用方法
我认为GCC扩展__attribute__((cleanup))
是一个好主意,至少在某些情况下,但我无法弄清楚如何以一种好的方式使用它。 我所做的一切看起来仍然很烦人。
我看到很多代码在做#define _cleanup_(x) __attribute__((cleanup(x))
只是为了输入less,但有没有办法传递一个标准函数,如free
或closedir
, fclose
等?
我看到我不能写:
__attribute__((cleanup(free))) char *foo = malloc(10);
因为清理回调会收到char**
指针,所以我必须总是这样写:
static void free_char(char **ptr) { free(*ptr); } __cleanup__((free_char)) char *foo = malloc(10);
这非常烦人,最烦人的部分是为你需要的所有类型定义这样的清理函数,因为显然你不能只为void **
定义它。 避免这些事情的最佳方法是什么?
这里有一个库,可以在__attribute__((cleanup))
之上构建通用智能指针( unique_ptr
和shared_ptr
): https : //github.com/Snaipe/libcsptr
它允许您编写更高级别的代码,如下所示:
#include #include #include void print_int(void *ptr, void *meta) { (void) meta; // ptr points to the current element // meta points to the array metadata (global to the array), if any. printf("%d\n", *(int*) ptr); } int main(void) { // Destructors for array types are run on every element of the // array before destruction. smart int *ints = unique_ptr(int[5], {5, 4, 3, 2, 1}, print_int); // ints == {5, 4, 3, 2, 1} // Smart arrays are length-aware for (size_t i = 0; i < array_length(ints); ++i) { ints[i] = i + 1; } // ints == {1, 2, 3, 4, 5} return 0; }
至于惯用语,但是? 那么上面肯定接近惯用的C ++。 不是那么多。 GCC和Clang显然主要支持该function,因为它们也有C ++编译器,所以他们可以选择在C前端使用RAII机器而无需额外费用。 以这种方式编写C-expected-as-C并不是一个好主意。 它有点依赖于存在的C ++编译器,尽管实际上并没有被使用 。
如果是我,我可能会调查实现自动释放池,或类似的东西,实际上可以在语言级别的纯C中完成。 取决于您需要多快地释放资源; 对于记忆,你通常可以在没有立即清理的情况下生活。
你不能写__attribute__((cleanup(free)))
,但你不需要为每种类型编写一个free
清理函数。 这很难看,但你可以这样写:
static void cleanup_free(void *p) { free(*(void**) p); }
我首先在systemd
代码库中看到了这一点。
对于其他函数,您通常需要编写一个带有额外级别间接的包装器,以便与__attribute__((cleanup))
。 systemd
为此定义了一个辅助宏 :
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ func(*p); \ } \ struct __useless_struct_to_allow_trailing_semicolon__
在整个地方使用 ,例如
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); #define _cleanup_pclose_ __attribute__((cleanup(pclosep)))