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();
};