C++ : other one simple scope guard

496 views Asked by At

Lets ask you about this simple scope guard:

template <class T>
struct finop_t {
    T& t;
    ~finop_t() { t(); }
};
#define FINALLY__(l, cl) \
    auto FIN ## l ## clo = cl; \
    finop_t<decltype(FIN ## l ## clo)> FIN ## l ## fin { FIN ## l ## clo}
#define FINALLY_(l, cl) FINALLY__(l, cl)
#define FINALLY(...) FINALLY_(__LINE__, ([=](){__VA_ARGS__}))

int main() {
    FINALLY( std::cout << "hello" << std::endl ; );
    std::cout << "one" << std::endl;
    FINALLY( std::cout << "world" << std::endl ; );
    std::cout << "second" << std::endl;
    return 0;
}

Is it safe to rely on destruction order here? i.e. is it safe assume that ~finop_t() will be called before lambda destructor?

2

There are 2 answers

13
Richard Hodges On

Destruction of local variables takes place in the inverse order of their construction.

And here's a more efficient way, which needs no references and uses copy-elision to construct the lambda in-place.

(note, you may want to consider [&] rather than [=], but that's for you to judge)

#include <iostream>

template <class T>
struct finop_t {
    finop_t(T&& t) : t(std::forward<T>(t)) {}
    T t;
    ~finop_t() { t(); }
};

template<class F>
finop_t<F> make_finop_t(F&& f)
{
    return finop_t<F>(std::forward<F>(f));
}

#define FINALLY__(l, cl) \
auto FIN ## l ## fin = make_finop_t(cl);

#define FINALLY_(l, cl) FINALLY__(l, cl)
#define FINALLY(...) FINALLY_(__LINE__, ([=](){__VA_ARGS__}))

int main() {
    FINALLY( std::cout << "hello" << std::endl ; );
    std::cout << "one" << std::endl;
    FINALLY( std::cout << "world" << std::endl ; );
    std::cout << "second" << std::endl;
    return 0;
}
0
avsmal On

Yes, it is safe. The macro stores a lambda in a local variable. The destruction order for local variables is fixed (in the reverse order of construction). Thus it is guaranteed that ~finop_t() destructor is called before the corresponding lambda (FIN ## l ## clo) destructor.