defer 关键字在 C/C++ 上的实现方案
前述:
golang 的关键字 defer 给出了一种,延时调用(RAII)的方式来释放资源。但是对于C/C++却没有内置这种方便的关键字。对于经常需要手动管理内存的C/C++尤其是C程序员这种特性显得无比重要。
这里给出了一种 C/C++ 下模拟实现 defer 的实现方案:
- 针对 C 语言基于 GCC/Clang 编译器扩展属性
cleanup
实现 - 针对 C++ 利用 RAII 来实现
头文件如下:(我只对 C/C++ 在 Linux 平台下 GCC/CLANG 编译器做了测试)
#ifndef __SCOPEGUARD_H__
#define __SCOPEGUARD_H__
#define __SCOPEGUARD_HELPER_IMPL(s1, s2) s1##_##s2
#define __SCOPEGUARD_HELPER(s1, s2) __SCOPEGUARD_HELPER_IMPL(s1, s2)
#if defined(__cplusplus)
#include <type_traits>
// ScopeGuard for C++11
namespace NSScopeGuard
{
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;
};
enum class ScopeGuardEnumHelper
{
};
template <typename Fun>
inline ScopeGuard<Fun> operator+(ScopeGuardEnumHelper, Fun &&fn)
{
return ScopeGuard<Fun>(std::forward<Fun>(fn));
}
} // namespace NSScopeGuard
// Helper macro
#define ON_SCOPT_EXIT \
auto __SCOPEGUARD_HELPER(RAII_AT_EXIT, __LINE__) = NSScopeGuard::ScopeGuardEnumHelper() + [&]()
#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 ON_SCOPT_EXIT(expr) \
void __SCOPEGUARD_HELPER(RAII_AT_EXIT, __LINE__)() { expr; } \
int __USELESS_VAR __attribute__((cleanup(__SCOPEGUARD_HELPER(RAII_AT_EXIT, __LINE__))));
#else
#error "platform not support!"
#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!");
ON_SCOPT_EXIT
{
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)
{
{
ON_SCOPT_EXIT(
printf("call defern");
);
printf("will quit scopen");
}
printf("before returnn");
return 0;
}
$ cc xx.c
$ ./a.out
will quit scope
call defer
before return
需要注意的是这里DEFER 宏展开后会发现,这里发生了函数的嵌套定义,经测试Clang 不支持函数嵌套定义,应次在头文件添加了对 llvm Clang独有编译器宏的判断。
此上C代码不能在安装Clang的Linux下编译(OSX不在此考虑范围,参考头文件__APPLE__宏)