便携式等效于gcc';s\uuuu属性(清理)

便携式等效于gcc';s\uuuu属性(清理),c,gcc,garbage-collection,scope,portability,C,Gcc,Garbage Collection,Scope,Portability,最近,我遇到了一个我发现非常有用的gcc扩展:\uuuuu属性\uuuuu(清理) 基本上,这允许您在局部变量退出作用域时为其分配清理调用。例如,给定以下代码部分,在调用foo的任何情况下都必须显式地维护和处理所有内存 void foo() { char * buff = ...; /* some memory allocation */ char * buff2 = 0, * buff3 = 0; if (! buff) { return; } else {

最近,我遇到了一个我发现非常有用的gcc扩展:
\uuuuu属性\uuuuu(清理)

基本上,这允许您在局部变量退出作用域时为其分配清理调用。例如,给定以下代码部分,在调用
foo
的任何情况下都必须显式地维护和处理所有内存

void foo() {
   char * buff = ...; /* some memory allocation */
   char * buff2 = 0, * buff3 = 0;
   if (! buff) {
      return;
   } else {
      buff2 = ...; /* memory allocation */
      if (! buff2) {
         goto clean_exit;
      } else {
         /* ... and so on ... */
      }
   }

clean_exit:
   free (buff);
   free (buff2);
   free (buff3);
}
但是,通过使用扩展,可以将

#define clean_pchar_scope __attribute__((cleanup(pchar_free)))

void pchar_free (char ** c) { free (*c); }

void foo () {
   char * buff clean_pchar_scope = ...; /* some memory allocation */
   char * buff2 clean_pchar_scope = 0, * buff3 clean_pchar_scope = 0;
   if (! buff)
      return;
   buff2 = ...; /* memory allocation */
   if (! buff2)
      return;
   /* and so on */
}
现在,所有内存都是在作用域的基础上回收的,而不需要使用嵌套的if/else或goto构造以及函数的统一内存释放部分。我意识到,对于更加嵌套的if/else构造,可以避免使用goto(因此,请不要在goto上进行圣战…),并且这个示例是人为的,但事实上,这是一个非常有用的特性

不幸的是,据我所知,这是gcc特有的。我对做同样事情的任何便携式方法感兴趣(如果它们存在的话)。 除了gcc,有没有人有过这样做的经验

编辑:
似乎便携性没有发挥作用。考虑到这一点,是否有一种方法可以在gcc空间之外实现这一点?这似乎是一个很好的特性,它是特定于gcc的…

在C中没有可移植的方式

幸运的是,C++是一个具有析构函数的标准特征。 编辑:

MSVC似乎有_try和_finally关键字,也可用于此目的。这与C++异常处理不同,我认为它可以在C.</P>中使用。
<>我想你会发现清理和尝试/最终没有被广泛使用,因为C++中的隐含支持,这是“足够接近”C,人们对行为的兴趣可以轻松地将代码转换成C++。

< P>你可以使用Booo::SyrdypTr用于这个目的。
boost::shared_ptr<char> buffer(p, cleanup);
boost::共享的ptr缓冲区(p,cleanup);

您的问题的前半部分是可移植的方式。

\uuuuu属性(清理)
不是特定于gcc的,clang和icc也支持它,这使得msvc成为唯一一个不支持它的主要编译器(而这对于现代C开发来说是毫无用处的)


因此,即使它不在ISO标准中,对于大多数实际用途,它也可以被认为是可移植的。

我们将利用这样一个事实,即for循环可以在每次迭代结束时工作,并且只在一次迭代中运行循环。最后我们要做的工作是增加计数器并调用函数来销毁对象。
x
参数是要限制其生存期的变量,
fn
参数是将破坏对象的函数或函数宏。
CONCATENATE
宏只是为了在嵌套
defer
时不会收到“shadowvariable”警告

#define CONCATENATE_DETAIL(x, y) x##y
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
#define defer(x, fn)                      \
  for (int CONCATENATE(i, __LINE__) = 0;  \
       CONCATENATE(i, __LINE__) == 0;     \
       CONCATENATE(i, __LINE__)++, fn((x)))
规则和限制:

  • defer
    宏有一个方便的特性,即嵌套时,它会以相反的顺序破坏对象;这就是C++所做的。
  • defer
    中使用变量之前,必须先声明该变量
  • 如果需要提前退出
    延迟
    块,则必须使用
    继续
    。使用
    break
    return
    将泄漏一个或多个对象
  • 如果您收到一条警告,提示在初始化变量之前使用了
    x
    ,很可能是因为您的一条代码路径直接指向销毁函数
  • 大多数公司和开源项目都不允许您使用此宏。(这可能是轻描淡写的说法。)
  • 这个列表不完整。始终测试您的代码,尤其是您在internet上找到的代码
下面是OP代码的扩展版本,使用
defer
宏。您应该能够直接将此代码放入并使用它

#include <stdio.h>
#include <stdlib.h>

#define CONCATENATE_DETAIL(x, y) x##y
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
#define defer(x, fn)                      \
  for (int CONCATENATE(i, __LINE__) = 0;  \
       CONCATENATE(i, __LINE__) == 0;     \
       CONCATENATE(i, __LINE__)++, fn((x)))

void cleanup(char* p) { free(p); }

void foo(void) {
  char* buff1 = NULL;
  char* buff2 = NULL;
  char* buff3 = NULL;

  defer(buff1, cleanup) {
    buff1 = malloc(64);
    if (buff1 == NULL)
      continue;
    defer(buff2, free) {
      buff2 = malloc(64);
      if (buff2 == NULL)
        continue;
      defer(buff3, free) {
        buff3 = malloc(64);
        if (buff3 == NULL)
          continue;

        buff1[63] = '\0';
        buff2[63] = '\0';
        buff3[63] = '\0';

        puts(buff1);
        puts(buff2);
        puts(buff3);
      }
    }
  }
}
#包括
#包括
#定义连接细节(x,y)x##y
#定义连接(x,y)连接_细节(x,y)
#定义延迟(x,fn)\
对于(int)串联(i,_线_)=0\
连接(i,_线_;)==0\
连接(i,_线_)++,fn((x)))
空洞清理(char*p){free(p);}
无效foo(无效){
char*buff1=NULL;
char*buff2=NULL;
char*buff3=NULL;
延迟(buff1,清理){
buff1=malloc(64);
如果(buff1==NULL)
继续;
延迟(buff2,免费){
buff2=malloc(64);
如果(buff2==NULL)
继续;
延迟(buff3,免费){
buff3=malloc(64);
如果(buff3==NULL)
继续;
buff1[63]='\0';
buff2[63]='\0';
buff3[63]='\0';
看跌期权(buff1);
看跌期权(2);
看跌期权(3);
}
}
}
}

defer
代码是我自己的,但是几年前我在Github上看到了类似的东西,但是现在我找不到了

@dpi说,GCC、clang和ICC支持
\uuuu属性(清理)

在此情况下,可以在大多数编译器中扩展到< C++ >代码>(清除),并返回到MSVC上的C++实现。它看起来像这样:

#if defined(__cplusplus)
  template<class F> struct finally{ 
    F f;
    ~finally() {f();}
  };

#  define WITH_CLEANUP(type, name, cleaner, ...)            \
   type name __VA_ARGS__;
   finally name # cleaner # __COUNTER__ = [&](cleaner(&name));
#elif
#  define WITH_CLEANUP(type, name, cleaner, ...)            \
      type name __attribute__(cleanup(cleaner)) __VA_ARGS__;
#endif
#如果已定义(uu cplusplus)
模板结构最终{
F;
~finally(){f();}
};
#使用清理定义(类型、名称、清理器等)\
类型名称\uuuu VA\u ARGS\uuuuuu;
最后命名为#清洁工#uuu计数器uuu=[&](清洁工(&name));
#埃利夫
#使用清理定义(类型、名称、清理器等)\
类型名称_属性_(清理(清理))_VA_参数_;;
#恩迪夫

有些关联:对于我所寻找的内容来说,这有点粗粒度。在这两种情况下,似乎解决方案仍然是GCC特定(对于接受的答案)或C++相关的基于类的答案。我了解
auto_ptr
boost::
设施,不幸的是它们不适用于C。好的,我已经更新了,忽略了可移植性问题。外
#if defined(__cplusplus)
  template<class F> struct finally{ 
    F f;
    ~finally() {f();}
  };

#  define WITH_CLEANUP(type, name, cleaner, ...)            \
   type name __VA_ARGS__;
   finally name # cleaner # __COUNTER__ = [&](cleaner(&name));
#elif
#  define WITH_CLEANUP(type, name, cleaner, ...)            \
      type name __attribute__(cleanup(cleaner)) __VA_ARGS__;
#endif