How do I run a cleanup code on the function exit?

安稳与你 提交于 2019-12-02 06:44:33

I have created macros that implement this functionality. They generate a local variable that runs a cleanup code in the destructor using C++11 lambda functions. The std::uncaught_exception function is used to check if there is any exception currently thrown. Creating the variable itself shouldn't throw any exceptions because a lambda with all variables captured by reference is used to create the variable (such lambdas do not throw exceptions in copy/move constructors).

#include <exception>

// An object of the class below will run an arbitrary code in its destructor
template <bool always, typename TCallable>
class OnBlockExit
{
public:
    TCallable m_on_exit_handler;

    ~OnBlockExit()
    {
        if (always || std::uncaught_exception())
            { m_on_exit_handler(); }
    }
};

// It is not possible to instantiate an object of the 'OnBlockExit' class
// without using the function below: https://stackoverflow.com/a/32280985/5447906.
// Creating of an object of the 'OnBlockExit' class shouldn't throw any exception,
// if lambda with all variables captured by reference is used as the parameter.
template <bool always, typename TCallable>
OnBlockExit<always, TCallable> MakeOnBlockExit(TCallable &&on_exit_handler)
{
    return { std::forward<TCallable>(on_exit_handler) };
}

// COMBINE is needed for generating an unique variable
// (the name of the variable contains the line number:
// https://stackoverflow.com/a/10379844/544790)
#define COMBINE1(X,Y) X##Y
#define COMBINE(X,Y) COMBINE1(X,Y)

// ON_BLOCK_EXIT generates a variable with the name
// in the format on_block_exit##__LINE__
#define ON_BLOCK_EXIT(always, code) \
    auto COMBINE(on_block_exit,__LINE__) = MakeOnBlockExit<always>([&]()code)

// Below are target macros that execute the 'code' on the function exit.
// ON_FINALLY will allways execute the code on the function exit,
// ON_EXCEPTION will execute it only in the case of exception.
#define ON_EXCEPTION(code) ON_BLOCK_EXIT(false, code)
#define ON_FINALLY(code)   ON_BLOCK_EXIT(true , code)

Here is an example how to use these macros:

void function(std::vector<const char*> &vector)
{
    int *arr1 = (int*)malloc(800*sizeof(int));
    if (!arr1) { throw "cannot malloc arr1"; }
    ON_FINALLY({ free(arr1); });

    int *arr2 = (int*)malloc(900*sizeof(int));
    if (!arr2) { throw "cannot malloc arr2"; }
    ON_FINALLY({ free(arr2); });

    vector.push_back("good");
    ON_EXCEPTION({ vector.pop_back(); });

    auto file = fopen("file.txt", "rb");
    if (!file) { throw "cannot open file.txt"; }
    ON_FINALLY({ fclose(file); });

    vector.push_back("bye");
    ON_EXCEPTION({ vector.pop_back(); });

    int *arr3 = (int*)malloc(1000*sizeof(int));
    if (!arr3) { throw "cannot malloc arr3"; }
    ON_FINALLY({ free(arr3); });

    arr1[1] = 1;
    arr2[2] = 2;
    arr3[3] = 3;
}

All cleanup code is executed in reverse order (in the order opposite to the order of the ON_FINALLY/ON_EXCEPTION macros appearance in the function). The cleanup code is executed only if control passes beyond the corresponding ON_FINALLY/ON_EXCEPTION macro.

Check the following link to see the output of the demo program execution: http://coliru.stacked-crooked.com/a/d6defaed0949dcc8

C++ has destructors which is what you need. An object that does whatever you need done at scope exit in its destructor that you then create an instance of on the stack in the scope where you need the work done, will get destroyed when the scope is left and then do the work at that time.

ScopeGuard is the right choice for you. It basically calls the function you specify at destructor.

So your code can be:

void your_function() {
  scope_guard guard = [&vector]() {
    vector.pop_back();
  };
  // your code
  guard.dismiss(); // success
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!