call function when leaving scope
Asked Answered
T

2

5

What is the most elegant solution for calling a function automatically when leaving a scope? My current approach (see below) works but I guess there should be something more general as writing a custom class for this.

#include <iostream>
#include <functional>

using namespace std;

class DoInDtor
{
public:
    typedef function<void()> F;
    DoInDtor(F f) : f_(f) {};
    ~DoInDtor() { f_(); }
private:
    F f_;
};

void foo()
{
    DoInDtor byeSayerCustom([](){ cout << "bye\n"; });

    auto cond = true; // could of course also be false
    if ( cond )
        return;

    return;
}

int main()
{
    foo();
}

Sure, one could abuse std::unique_ptr and its custom deleter, but since I am not really acquiring a resource here, that does not sound great to me either in terms of code readability. Any suggestions?

Tunny answered 26/3, 2014 at 9:26 Comment(10)
This probably belongs on Code Review.Ln
When leaving a function or leaving a scope? Because, there are two different things. Not that it matters a lot. But sounds heavy work to put this DoInDtor object in each scope. What are you trying to achieve?Constipate
As a side note, you generally should not do this if the function that you want to be called when leaving the scope can throw an exception!Nonplus
@KerrekSB: OK, sorry. I will post it there is no answers will be posted here.Tunny
You might want to check out (Loki's) (ScopeGuard) class.Aldas
@KirilKirov: Any scope, nur just function scope. I edited my example to make this clearer.Tunny
@anderas: Yes, dtors sould of course never throw.Tunny
@Angew: Thanks, this looks interesting.Tunny
In the future, we might get this: Scoped Resource - Generic RAII Wrapper for the Standard Library.Siderosis
@CassioNeri: That sounds nice, thanks.Tunny
R
2

Using a constructor/destructor in this way is a common way to solve this sort problem. I have used it for both locks (constructor takes lock, destructor releases it) and logging purposes (constructor prints something on construction, and destructor prints on destruction, giving a nice callgraph of a project - in the latter case, using macros to also get __FILE__ and __LINE__ store in the object, so we can see where the constructor was called [it's almost impossible to do this for the destructor, but typically it's possible to see the constructor and make out where the destructor gets called]).

Regal answered 26/3, 2014 at 9:39 Comment(0)
P
3

Angew and Cassio are pointing you in the right direction here with ScopeGuard. Using std::function can incur dynamic memory allocation and is not the most efficient solution. The ScopeGuard-based implementations avoid that by encoding the type of the function object to invoke into the ScopeGuard class itself as a template parameter. With a helper function template, you never have to explicitly state that type, the compiler deduces it for you (and has to in the case of a lambda being used as the function object to invoke).

Rather than repeating all the details here, I recently wrote two articles about this very area:

The first article focuses on simplicity and starts from a similar place as your original question, including a motivating example. The second one goes through all the steps to evolve that to an efficient and robust implementation which results in concise, very readable code. The second article also explains how the material presented relates to Andrei's work with ScopeGuard and the C++ standards proposal for scope_exit mentioned by Cassio.

Precondition answered 9/6, 2015 at 21:59 Comment(0)
R
2

Using a constructor/destructor in this way is a common way to solve this sort problem. I have used it for both locks (constructor takes lock, destructor releases it) and logging purposes (constructor prints something on construction, and destructor prints on destruction, giving a nice callgraph of a project - in the latter case, using macros to also get __FILE__ and __LINE__ store in the object, so we can see where the constructor was called [it's almost impossible to do this for the destructor, but typically it's possible to see the constructor and make out where the destructor gets called]).

Regal answered 26/3, 2014 at 9:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.