C++实现Go中的defer

  • 原理:利用栈和析构

    技巧:

    1. 利用__LINE__ 宏定义栈变量
    2. 利用C++变量名的就近原则,区分是否使用 init_defer_func_stack()
    3. 利用重载函数匹配规则,根据是否使用init_defer_func_stack,获取临时栈变量或者init_defer_func_stack中的变量
    4. 利用运算符重载,使得宏可以拼接lambda表达式
    5. 利用变参宏,使得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 就行了
    }