前述:

go 中defer 给出了一种,延时调用的方式来释放资源。但是对于C/C++去没有内置的这种属性。对于经常手动管理内存的C/C++有其是C程序员这种特性显得无比重要。这里给出了一种基于GCC/Clang 编译器扩展属性(cleanup)来模拟实现defer的一种方式。

头文件如下:(我只对C/C++在GCC/CLANG Linux 平台做了测试,Object-C没有测试)

#ifndef __SCOPEGUARD_H__
#define __SCOPEGUARD_H__ #define __SCOPEGUARD_CONCATENATE_IMPL(s1, s2) s1##s2
#define __SCOPEGUARD_CONCATENATE(s1, s2) __SCOPEGUARD_CONCATENATE_IMPL(s1, s2) #if defined(__cplusplus)
#include <type_traits> // ScopeGuard for C++11
namespace clover
{
template <typename Fun>
class ScopeGuard
{
public:
ScopeGuard(Fun &&f) : _fun(std::forward<Fun>(f)), _active(true) {} ~ScopeGuard()
{
if (_active)
{
_fun();
}
} void dismiss() { _active = false; } ScopeGuard() = delete;
ScopeGuard(const ScopeGuard &) = delete;
ScopeGuard &operator=(const ScopeGuard &) = delete;
ScopeGuard(ScopeGuard &&rhs) : _fun(std::move(rhs._fun)), _active(rhs._active) { rhs.dismiss(); } private:
Fun _fun;
bool _active;
}; namespace detail
{
enum class ScopeGuardOnExit
{
}; template <typename Fun>
inline ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun &&fn)
{
return ScopeGuard<Fun>(std::forward<Fun>(fn));
}
} // namespace detail
} // namespace clover // Helper macro
#define DEFER \
auto __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) = \
clover::detail::ScopeGuardOnExit() + [&]() #elif defined(__APPLE__)
/* ScopeGuard for Objective-C */
typedef void (^ext_cleanupBlock_t)(void);
static inline void ext_executeCleanupBlock(__strong ext_cleanupBlock_t *block) { (*block)(); }
#define DEFER \
__strong ext_cleanupBlock_t __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) \
__attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^ #elif (defined(__linux__) || defined(__ANDROID__)) && !defined(__llvm__)
/**
* Linux(HOST) GCC does not support extension 'blocks' and keyword '__strong'
* So, just use cleanup in plain way
*/
#define DEFER(expr) \
void __SCOPEGUARD_CONCATENATE(__FUNC_, __LINE__)(void *_arg) \
{ \
(void)_arg; \
expr \
} \
int __USELESS_V __attribute__((cleanup(__SCOPEGUARD_CONCATENATE(__FUNC_, __LINE__)))); #endif #endif //__SCOPEGUARD_H__

测试:

不需要过多的解释,直接看执行结果就知道了。当超出作用域(break,goto,return)之后,会自动调用指定的内存释放函数(当然这里也可以用其他函数来代替)。()

C++:

#include <iostream>
#include <string>
#include "scopeguard.h"
using namespace std; int main()
{
string *as = new string("hello world!");
DEFER
{
cout << "in scope guard" << endl;
delete as;
};
cout << "at end" << endl;
return 0;
} $ c++ xx.c
$ ./a.out
at end
in scope guard

C:

#include <stdio.h>
int main(int argc, char **argv)
{
{
DEFER(
printf("call defer\n");
);
printf("will quit scope\n");
}
printf("before return\n");
return 0;
}
$ cc xx.c
$ ./a.out
will quit scope
call defer
before return

需要注意的是这里DEFER 宏展开后会发现,这里发生了函数的嵌套定义,经测试Clang 不支持函数嵌套定义,应次在头文件添加了对 __llvm__ Clang独有编译器宏的判断。

此上C代码不能在安装Clang的Linux下编译(OSX不在此考虑范围,参考头文件__APPLE__宏)

参考:

https://zhuanlan.zhihu.com/p/21303431

https://zhuanlan.zhihu.com/p/35191739

defer implement for C/C++ using GCC/Clang extension的更多相关文章

  1. c 各种编译器(gcc clang)

    很多时候,出现一些类似GNU,GCC,CLANG,LLVM等与编译器有关的名词的时候,都不太清楚它到底是干嘛的,理解这些东西后, 对于xcode中很多配置型的需求修改起来都会得心应手,因此有必要了解透 ...

  2. CentOS安装gcc clang git mysql等软件高版本福利

    最近同事需要在单位提供的开发机上临时安装gcc等软件,时间紧迫,因此向其推荐安装福利devtoolset. 感谢devtoolset,使得CentOS软件安装无比的快捷,卸载也是无与伦比的简单. gc ...

  3. 编译器:gcc, clang, llvm

    clang Clang是LLVM的前端,可以用来编译C,C++,ObjectiveC等语言.传统的编译器通常分为三个部分,前端(frontEnd),优化器(Optimizer)和后端(backEnd) ...

  4. gcc/clang编译带pthread.h头文件的源码时需要的参数

    今天敲了一个小程序,编译时出现错误:undefined reference pthread_create 原来由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, ...

  5. clang和gcc消除警告

    1. clang命令,它的作用是用来消除特定区域的clang的编译警告,-Wgnu则是消除?:警告, 例: #pragma clang diagnostic push #pragma clang di ...

  6. linux下Clang和gcc的区别

    Clang 比 GCC 编译器的优势: 编译速度更快 编译产出更小 出错提示更友 好,比如 clang 在编译过程可以直接指出相对简单的出错位置以及它 “ 认为 ” 正确的方式 . 内置有静态分析工具 ...

  7. clang编译器简介

    本文部分内容引用: 中文维基百科. 结构化编译器前端--clang介绍. 什么是clang编译器? clang是LLVM编译器工具集的一个用于编译C.C++.Objective-C的前端.LLVM项目 ...

  8. GCC编译器ABI

    ABI与EABI 1)ABI(Application Binary Interface for the ARM Architecture),描述了应用程序与cpu内核的低级接口. ABI允许编译好的目 ...

  9. 各种GCC

    Cross GCC Cygwin GCC Linux GCC MacOSX GCC MinGW GCC Solaris GCC Clang

随机推荐

  1. IntelliJ Idea如何解决Could not autowire. No beans of 'xxxx' type found的错误提示

    问题描述 在idea中进行开发时,经常会遇见Could not autowire. No beans of 'xxxx' type found的错误提示,这样的是不影响程序编译和运行的,但是看起来会很 ...

  2. HotSpot的执行引擎-CallStub栈帧

    之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...

  3. Android ActivityResumeTrigger: not whiteListed

    在点击返回按钮的时候报错: ActivityResumeTrigger: not whiteListed 合作者写的返回操作是: findViewById(R.id.lin_back).setOnTo ...

  4. Gitlab-CI/CD 1

    Gitlab-Runner自动构建服务器搭建1 这里讲到的gitlab仓库指的是https://gitlab.com/,自建gitlab仓库也基本相同. 项目的构建打包过程相对比较消耗系统性能,所以g ...

  5. 数据结构与算法笔记(java)目录

    数据结构: 一个动态可视化数据结构的网站 线性结构 数组 动态数组 链表 单向链表 双向链表 单向循环链表 双向循环链表 栈 栈 队列 队列 双端队列 哈希表 树形结构 二叉树 二叉树 二叉搜索树 A ...

  6. centos7下的redis集群模式

    1.先安装好单机版的redis 2.Reids安装包里有个集群工具,要复制到/usr/local/bin里去 cd /home/redis/redis-4.0./src ls - cp redis-t ...

  7. MariaDB二进制安装

    下载二进制的MariaDB https://downloads.mariadb.org/mariadb/10.2.16/ 安装过程 下载&解压 下载到/tools安装到/application ...

  8. edge 修改链接打开方式

    我目前的edge版本是 Version 84.0.522.63 (Official build) (64-bit) 每次点击链接, 都是默认在原页面打开新标签, 不符合过往习惯. 修改方式 打开控制面 ...

  9. SEO需要分析哪些网站数据

    http://www.wocaoseo.com/thread-227-1-1.html 一.网站的基本数据 1.网站流量详情(ip.pv.需要看pv与ip的比) 2.网站的跳出率(可以看出一个网站的用 ...

  10. 使用 Postman 做 API 自动化测试

    Postman 最基本的功能用来重放请求,并且配合良好的 response 格式化工具. 高级点的用法可以使用 Postman 生成各个语言的脚本,还可以抓包,认证,传输文件. 仅仅做到这些还不能够满 ...