C++实现Go中的defer
原理:利用栈和析构
技巧:
- 利用
__LINE__
宏定义栈变量 - 利用C++变量名的就近原则,区分是否使用
init_defer_func_stack()
- 利用重载函数匹配规则,根据是否使用
init_defer_func_stack
,获取临时栈变量或者init_defer_func_stack
中的变量 - 利用运算符重载,使得宏可以拼接lambda表达式
- 利用变参宏,使得
defer_func
用法和std::bind
一致
// // defer.hpp // ~~~~~~~~~ // Copyright (c) 2021 Gwkang All rights reserved. // #ifndef DEFER_HPP #define DEFER_HPP #include <list> #include <functional> #define STRINGCAT_HELPER(x, y) x ## y #define STRINGCAT(x, y) STRINGCAT_HELPER(x, y) #define init_defer_var_name __defer_stack__ #define defer_stack_type DeferStack #define defer_member_func add_defer #define defer_exec_func exec #define defer_helper_func DeferStack::defer_helper #define defer_exec_helper_func DeferStack::exec_helper #ifdef return #error"return is defined!" #undef return #endif #define return if (defer_exec_helper_func(init_defer_var_name), true) return #define defer_var_name STRINGCAT(__defer__, __LINE__) #define defer_helper_name STRINGCAT(__helper__, __LINE__) #define defer_define_helper(var_name) \ defer_stack_type var_name; \ defer_helper_func(var_name, init_defer_var_name) // 定义局部 DeferStack 对象 #define init_defer_func_stack() defer_stack_type init_defer_var_name // 表达式(如果没使用init_defer_func_stack,则引用捕获,若使用了init_defer_func_stack,则值捕获) //#ifdef return //#define defer_expr(x) defer_expr_ref(x) //#else //#define defer_expr(x) \ // defer_stack_type defer_var_name; \ // defer_stack_type &defer_helper_name = defer_helper_func(defer_var_name, init_defer_var_name); \ // if (&defer_helper_name == &defer_var_name) defer_helper_name+[&]{x;}; \ // else defer_helper_name+[=]()mutable{x;}; //#endif // return // 表达式(引用捕获) #define defer_expr_ref(x) defer_define_helper(defer_var_name)+[&]{x;} // 表达式(值捕获) #define defer_expr_val(x) defer_define_helper(defer_var_name)+[=]()mutable{x;} // 函数 #define defer_func(func, ...) defer_define_helper(defer_var_name).defer_member_func(func, ##__VA_ARGS__) // lambda #define defer_lambda defer_define_helper(defer_var_name)+ // 不使用,稻草人标识符 enum { init_defer_var_name }; class DeferStack { std::list<std::function<void()>> defer_func_stack; public: ~DeferStack() { exec(); } void exec() { while (!defer_func_stack.empty()) { defer_func_stack.front()(); defer_func_stack.pop_front(); } } template<typename Function, typename ... Args> inline void add_defer(Function && f, Args && ... args) { defer_func_stack.emplace_front(std::bind(f, std::forward<Args>(args)...)); } template<typename Function> inline void operator+(Function && f) { defer_func_stack.emplace_front(f); } static constexpr DeferStack &defer_helper(DeferStack &, DeferStack &local_defer) { return local_defer; } static constexpr DeferStack &defer_helper(DeferStack &new_defer, const decltype(::init_defer_var_name) &) { return new_defer; } static inline void exec_helper(DeferStack &local_defer) { local_defer.exec(); } static constexpr void exec_helper(const decltype(::init_defer_var_name) &) { } }; #endif // DEFER_HPP
- 利用
用法
struct TestDefer { TestDefer(int i) : i(i) { std::cout << this << std::endl; } ~TestDefer() { std::cout << this << " ~MyStruct()" << std::endl; } int i; #define return if (defer_exec_helper_func(init_defer_var_name), true) return void print() const { std::cout << this << " " << i << std::endl; } }; void test() { // 如果使用了 init_defer_func_stack,后续的 defer 操作在 init_defer_func_stack 出作用域后执行 // 如果没使用 init_defer_func_stack,则 defer 操作在出作用域后立刻执行 init_defer_func_stack(); // 局部作用域 { std::shared_ptr<TestDefer> a = std::make_shared<TestDefer>(1); std::shared_ptr<TestDefer> b = std::make_shared<TestDefer>(2); defer_expr(a->print()); defer_expr_ref(b->print()); // warning: 需要注意表达式中变量的生命周期(此时b是垂悬引用) defer_expr_val(b->print()); defer_func(&TestDefer::print, a); defer_lambda[=]{ b->print(); }; } // 体现调用顺序 std::cout << "test" << std::endl; }
defer 应用场景
当一个函数有多个出口时,defer会极大的减少重复代码
例如:
bool test_no_defer() { FILE *fp1 = fopen("1.txt", "r"); if (!fp1) { return false; } FILE *fp2 = fopen("2.txt", "r"); if (!fp2) { fclose(fp1); return false; } FILE *fp3 = fopen("3.txt", "r"); if (!fp3) { fclose(fp1); fclose(fp2); return false; } // ... 读取文件,函数如果有多个出口,每个出口都要 调用三次fclose() } // 使用 defer bool test_defer() { FILE *fp1 = fopen("1.txt", "r"); if (!fp1) return false; defer_expr(fclose(fp1)); FILE *fp2 = fopen("2.txt", "r"); if (!fp2) return false; defer_expr(fclose(fp2)); FILE *fp3 = fopen("3.txt", "r"); if (!fp3) return false; defer_expr(fclose(fp3)); // ... 读取文件,函数如果有多个出口,直接return 就行了 }