scope_exit 的 11 实现

#ifndef SCOPE_EXIT_HPP
#define SCOPE_EXIT_HPP
#include "pp_cat.h"
#include <functional>

#define SCOPE_EXIT_CLASSNAME(id) PP_CAT(__unique_classname__, id)
#define SCOPE_EXIT_INSTANCENAME(id) PP_CAT(__unique_instance__, id)

#define SCOPE_EXIT(...) struct SCOPE_EXIT_CLASSNAME(__LINE__) {
    typedef std::function<void(void)> fun; 
    fun f_; 
            
    SCOPE_EXIT_CLASSNAME(__LINE__) (fun f) :f_(f) { } 
    ~SCOPE_EXIT_CLASSNAME(__LINE__) () { f_(); } 
} SCOPE_EXIT_INSTANCENAME(__LINE__) ([__VA_ARGS__]()

#define SCOPE_EXIT_END );

#endif // SCOPE_EXIT_HPP

设计思路见 boost. 主要的方法是利用 析构函数, 在退出 scope 时自动执行代码.
1. 为了使 SCOPE_EXIT 里的代码不自动执行, 预处理是必须的. 为了使 SCOPE EXIT 里的代码能迟滞执行, 必须保存它到一个函数, 经由析构函数调用它. 函数里面定义函数是非法的, 但函数里面定义结构体是合法的.

所以: 可以在函数里面定义结构体. (由此就找到一个存放代码的方式了)

2, 为了使一个 SCOPE 里能有多个 SCOPE_EXIT, 必须给每一个 instance 一个特殊的 id.
这个 id 不由用户指定, 解决方法 只能是 预处理.

不能使用自增式的 macro 的方式使得 每一个 instance 有唯一标号. 理由是, 目前, 自增式的 macro 只能通过 #include 来拿到

e.g

#include "pp_auto_inc.h" // expand to nothing
PP_AUTO_INC // expand to 1
#include "pp_auto_inc.h"
PP_AUTO_INC // expand to 2

这条排除后很难想象有啥东西能提供唯一的 id. 但其实有一个宏很好用. __LINE__. 写入标准, 所以代码完全合法. 能保证在该编译单元内只有唯一 id. 还要注意, PP_CAT 是让 __LINE__ 被显式 expand 所必须的

最后是改进: 我的代码不够好, 可以改的地方毛想想还挺多的. 因为是 C++11, 实际上 可以把定义在 scope 里的结构体单独弄出来了. 而且 scope_exit_end 这个宏应该是能消除掉的. 然后还没考虑过在 namespace, class 这种地方能不能用( 应该不能让它能用? )

下面是测试代码

#include <iostream>
#include "scope_exit.hpp"
using namespace std;

int* foo() {
    int* p;
    const char * str = "hello world";

    SCOPE_EXIT(&p) {
        cout << "dtor1n";
        cout << p << endl;
        delete p;
        p = 0;
        cout << p << endl;
        cout << "dtor1 endn";
    } SCOPE_EXIT_END

    SCOPE_EXIT(&) {
        cout << str << endl;
        str = nullptr;
    } SCOPE_EXIT_END

    p = new int(10);
    cout << p << endl;
    cout << *p << endl;

    return p;
}

int main() {
    cout << foo();
}

Leave a Reply

Your email address will not be published. Required fields are marked *