golang中有个处理异常的关键字defer
应用场景类似于java里面的finally,使用的时候就是所有的其他的正常的函数进程执行完毕之后都要执行defer。也就是被defer修饰的函数或者语句都是等到所有的作用域内部的函数执行完毕才会执行。
defer的这个特性正好契合C++的RAII。那我们能不能利用RAII实现defer的功能呢。
思路
RAII
我们知道对象在作用域结束自动析构,那我们在使用defer的时候创建某个对象,指定某个操作,然后在析构的时候执行这个操作。
std::function
C++11引入了std::function
、std::bind
和lambda
,我们可以利用这些将需要defer的操作包装成std::function<void()>
对象存储到一个对象中,当对象析构时,调用这个std::function<void()>
对象。
如何像关键字一样使用?
golang的使用方式时这样的
defer file.close();
按照之前的思路,C++的使用方式是这样的
defer_t defer([&file]() {
file.close();
});
或者
defer_t defer;
defer.set([&file]() {
file.close();
});
这个和golang的使用方式都相去甚远,关键是不易用。
那我们一个一个问题解决。
首先,每次都自己定义个变量太麻烦。能不能自动定义?
宏
defer auto DEFER_CREATE_NAME(defer_, __COUNTER__)
这个defer_name每次使用生成不一样呢?
使用宏生成不同名称
#define DEFER_CONCAT_NAME(l, r) l##r
#define DEFER_CREATE_NAME(l, r) DEFER_CONCAT_NAME(l, r)
#define defer auto DEFER_CREATE_NAME(defer_, __COUNTER__)
还有个问题,如何传入std::function
呢?
operator+-*/
我们定义一个操作符,使用操作符添加std::function
。
实现
class defer_t final {
public:
template <typename F>
explicit defer_t(F&& f) noexcept : f_{std::forward<F>(f)} {
}
~defer_t() {
if (f_) {
f_();
}
}
defer_t(const defer_t&) = delete;
defer_t& operator=(const defer_t&) = delete;
defer_t(defer_t&& rhs) noexcept : f_{std::move(rhs.f_)} {
}
defer_t& operator=(defer_t&& rhs) noexcept {
f_ = std::move(rhs.f_);
return *this;
}
private:
std::function<void()> f_;
};
class defer_maker final {
public:
template <typename F>
defer_t operator+(F&& f) {
return defer_t{std::forward<F>(f)};
}
};
#define DEFER_CONCAT_NAME(l, r) l##r
#define DEFER_CREATE_NAME(l, r) DEFER_CONCAT_NAME(l, r)
#define defer auto DEFER_CREATE_NAME(defer_, __COUNTER__) = defer_maker{} +
#define defer_scope defer [&]
使用
defer [&file]() {
file.close();
};
defer_scope {
file.close();
};