defer 关键字在 C/C++ 上的实现方案

前述:

golang 的关键字 defer 给出了一种,延时调用(RAII)的方式来释放资源。但是对于C/C++却没有内置这种方便的关键字。对于经常需要手动管理内存的C/C++尤其是C程序员这种特性显得无比重要。
这里给出了一种 C/C++ 下模拟实现 defer 的实现方案:

  1. 针对 C 语言基于 GCC/Clang 编译器扩展属性cleanup实现
  2. 针对 C++ 利用 RAII 来实现

头文件如下:(我只对 C/C++ 在 Linux 平台下 GCC/CLANG 编译器做了测试)

#ifndef __SCOPEGUARD_H__
#define __SCOPEGUARD_H__

#define __SCOPEGUARD_HELPER_IMPL(s1, s2) s1##_##s2
#define __SCOPEGUARD_HELPER(s1, s2) __SCOPEGUARD_HELPER_IMPL(s1, s2)

#if defined(__cplusplus)
#include <type_traits>

// ScopeGuard for C++11
namespace NSScopeGuard
{
    template <typename Fun>
    class ScopeGuard
    {
    public:
        ScopeGuard(Fun &&f) : _fun(std::forward<Fun>(f)), _active(true) {}

        ~ScopeGuard()
        {
            if (_active)
                _fun();
        }

        void dismiss() { _active = false; }

        ScopeGuard() = delete;
        ScopeGuard(const ScopeGuard &) = delete;
        ScopeGuard &operator=(const ScopeGuard &) = delete;
        ScopeGuard(ScopeGuard &&rhs) : _fun(std::move(rhs._fun)), _active(rhs._active) { rhs.dismiss(); }

    private:
        Fun _fun;
        bool _active;
    };

    enum class ScopeGuardEnumHelper
    {
    };

    template <typename Fun>
    inline ScopeGuard<Fun> operator+(ScopeGuardEnumHelper, Fun &&fn)
    {
        return ScopeGuard<Fun>(std::forward<Fun>(fn));
    }
} // namespace NSScopeGuard

// Helper macro
#define ON_SCOPT_EXIT \
    auto __SCOPEGUARD_HELPER(RAII_AT_EXIT, __LINE__) = NSScopeGuard::ScopeGuardEnumHelper() + [&]()

#elif (defined(__linux__) || defined(__ANDROID__)) && !defined(__llvm__)
/**
 * Linux(HOST) GCC does not support extension 'blocks' and keyword '__strong'
 * So, just use cleanup in plain way
 */
#define ON_SCOPT_EXIT(expr)                                      \
    void __SCOPEGUARD_HELPER(RAII_AT_EXIT, __LINE__)() { expr; } \
    int __USELESS_VAR __attribute__((cleanup(__SCOPEGUARD_HELPER(RAII_AT_EXIT, __LINE__))));

#else
#error "platform not support!"
#endif

#endif //__SCOPEGUARD_H__

测试:

不需要过多的解释,直接看执行结果就知道了。当超出作用域(break,goto,return)之后,会自动调用指定的内存释放函数(当然这里也可以用其他函数来代替)。()

C++:

#include <iostream>
#include <string>
#include "scopeguard.h"
using namespace std;

int main()
{
    string *as = new string("hello world!");
    ON_SCOPT_EXIT
    {
        cout << "in scope guard" << endl;
        delete as;
    };
    cout << "at end" << endl;
    return 0;
}

$ c++ xx.c
$ ./a.out
at end
in scope guard

C:

#include <stdio.h>
int main(int argc, char **argv)
{
    {
        ON_SCOPT_EXIT(
            printf("call defern");
        );
        printf("will quit scopen");
    }
    printf("before returnn");
    return 0;
}
$ cc xx.c
$ ./a.out 
will quit scope
call defer
before return

需要注意的是这里DEFER 宏展开后会发现,这里发生了函数的嵌套定义,经测试Clang 不支持函数嵌套定义,应次在头文件添加了对 llvm Clang独有编译器宏的判断。

此上C代码不能在安装Clang的Linux下编译(OSX不在此考虑范围,参考头文件__APPLE__宏)

参考:

https://zhuanlan.zhihu.com/p/21303431

https://zhuanlan.zhihu.com/p/35191739

posted @ 2019-10-14 17:45  sinpo828  阅读(1094)  评论(0编辑  收藏  举报