便携式等效于gcc';s\uuuu属性(清理)
最近,我遇到了一个我发现非常有用的gcc扩展:便携式等效于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 {
\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)))
规则和限制:
宏有一个方便的特性,即嵌套时,它会以相反的顺序破坏对象;这就是C++所做的。defer
- 在
中使用变量之前,必须先声明该变量defer
- 如果需要提前退出
块,则必须使用延迟
。使用继续
或break
将泄漏一个或多个对象return
- 如果您收到一条警告,提示在初始化变量之前使用了
,很可能是因为您的一条代码路径直接指向销毁函数x
- 大多数公司和开源项目都不允许您使用此宏。(这可能是轻描淡写的说法。)
- 这个列表不完整。始终测试您的代码,尤其是您在internet上找到的代码
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