使用GCC和clang _属性_((cleanup))和指针声明的一种好方法

使用GCC和clang _属性_((cleanup))和指针声明的一种好方法,c,gcc,clang,C,Gcc,Clang,我认为GCC扩展是一个好主意,至少在某些情况下是这样,但我不知道如何以一种好的方式使用它。我所做的一切看起来仍然很烦人 我看到很多代码都在做#define(cleanup)(x)(cleanup)(x))这样的标准函数,比如free或closedir,fclose,等等 在我看来,我不能只写: __attribute__((cleanup(free))) char *foo = malloc(10); 因为清理回调将接收char**指针,我必须始终编写如下内容: static void fre

我认为GCC扩展是一个好主意,至少在某些情况下是这样,但我不知道如何以一种好的方式使用它。我所做的一切看起来仍然很烦人

我看到很多代码都在做
#define(cleanup)(x)(cleanup)(x)
)这样的标准函数,比如
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**
定义它。避免这些事情的最佳方法是什么?

你不能编写
\uu属性(清理(免费)))
,但您不需要为每种类型编写
免费的
清理功能。这很难看,但您可以编写以下内容:

static void cleanup_free(void *p) {
  free(*(void**) p);
}
我第一次看到这个

对于其他函数,通常需要编写一个具有额外间接级别的包装器,以便与
\uuuu attribute\uuuu((cleanup))
systemd

使用哪一个,例如


有一个库可以在
\uu属性(清理))
上构建通用智能指针(
unique\u ptr
shared\u ptr
),如下所示:

它允许您编写如下更高级别的代码:

#include <stdio.h>
#include <csptr/smart_ptr.h>
#include <csptr/array.h>

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;
}
#包括
#包括
#包括
void print_int(void*ptr,void*meta){
(无效)元;
//ptr指向当前元素
//meta指向数组元数据(数组的全局元数据,如果有)。
printf(“%d\n”,*(int*)ptr);
}
内部主(空){
//数组类型的析构函数在
//破坏前的阵列。
智能int*ints=unique_ptr(int[5],{5,4,3,2,1},print_int);
//ints=={5,4,3,2,1}
//智能阵列具有长度感知能力
对于(大小i=0;i<数组长度(ints);+i){
ints[i]=i+1;
}
//ints=={1,2,3,4,5}
返回0;
}
<> P> >习惯用法,这是肯定的,接近于习惯的C++。C不太重要。这个特性显然主要支持于GCC和Clang,因为它们也有C++编译器,所以他们可以选择在C前端使用RAII机制,而不需要额外的费用;这并不意味着编写C-Actudi-AS-C TH是一个好主意。是一种方法,它依赖于C++编译器的存在,尽管实际上没有被使用。
如果是我,我可能会研究实现自动释放池,或者类似的东西,这些东西实际上可以在纯C语言级别上完成。这取决于您需要多快地释放资源;对于内存,您通常不需要立即清理就可以生存。

我想您已经提出了一个很好的理由来说明
\u属性__((清理))
不是一个好主意。如果你想要析构函数,你知道在哪里可以找到它们。@user3386109,我会说这是一个好主意,但实现有点失败。或者可能有什么我不明白的地方,这就是我问的原因。@coredump实现也很好。清理函数需要一个指向变量的指针是有意义的,因为变量本身可能不是指针,而是一个较大的结构,最好不要通过值传递(参见glib的
g_autoptr
g_auto
之间的区别)有两个不同的属性只是为了方便,不必膨胀语言扩展。@ RossRidge:最好控制它,而不是担心C++中的行李带来的问题。所以,如果我们想要合理的、好的、非隐藏的析构函数,我们知道在哪里找到它们:GCC和CLAN下的C代码。代码>清理_free
执行从
T**
=>
void*
=>
void**
的转换,这就是为什么需要显式的
*(void**)
舞蹈的原因。
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);

#define _cleanup_pclose_ __attribute__((cleanup(pclosep)))
#include <stdio.h>
#include <csptr/smart_ptr.h>
#include <csptr/array.h>

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;
}