1 #pragma once
2 
3 #include <functional>
4 
5 /** Type that gives an lvalue a dynamically-scoped temporary value. An
6  *  unwind_var wraps a variable or other writable lvalue, assigns it a
7  *  temporary value, and restores the original (or a specified) value when
8  *  the unwind_var goes out of scope or is otherwise destroyed.
9  */
10 template <typename T>
11 class unwind_var
12 {
13 public:
14     /** Wrap the lvalue val_ and on unwinding restore its original value. */
unwind_var(T & val_)15     unwind_var(T &val_) : val(val_), oldval(val_) { }
16 
17     /** Wrap the lvalue val_, assign it the temporary value newval, and
18      *  on unwinding restore its original value.
19      */
unwind_var(T & val_,T newval)20     unwind_var(T &val_, T newval) : val(val_), oldval(val_)
21     {
22         val = newval;
23     }
24 
25     /** Wrap the lvalue val_, assign it the temporary value newval, and
26      *  on unwinding assign it the value reset_to.
27      */
unwind_var(T & val_,T newval,T reset_to)28     unwind_var(T &val_, T newval, T reset_to) : val(val_), oldval(reset_to)
29     {
30         val = newval;
31     }
32 
~unwind_var()33     ~unwind_var()
34     {
35         val = oldval;
36     }
37 
38     /** Get the current (temporary) value of the wrapped lvalue. */
value()39     T value() const
40     {
41         return val;
42     }
43 
44     /** Get the value that will be used to restore the wrapped lvalue. */
original_value()45     T original_value() const
46     {
47         return oldval;
48     }
49 
50 private:
51     T &val;
52     T oldval;
53 };
54 
55 typedef unwind_var<bool> unwind_bool;
56 
57 /** Type to call a function when an instance goes out of scope or is
58  * otherwise destroyed.
59  */
60 class unwinder
61 {
62 public:
63     /** Construct an unwinder that calls cleanup_fn when destroyed. */
64     // We templatise the parameter, rather than taking a function<>, so that
65     // we can write "unwinder foo = [](){};" without running into the
66     // "only one user-defined conversion at a time" rule.
67     template<class Fn>
unwinder(Fn cleanup_fn)68     unwinder(Fn cleanup_fn) : cleaner(cleanup_fn) { }
69 
~unwinder()70     ~unwinder()
71     {
72         if (cleaner)
73             cleaner();
74     }
75 
76     /** Cancel this unwinder, so that it calls nothing when destroyed. */
cancel()77     void cancel()
78     {
79         cleaner = nullptr;
80     }
81 private:
82     function<void ()> cleaner;
83 };
84 
85 // Preprocessor tricks, ugh.
86 #define CONCAT_IMPL(x, y) x##y
87 #define CONCAT_TOK(x, y) CONCAT_IMPL(x, y)
88 
89 /** Set up a block of code to run when the current scope is exited.
90  *
91  * Usage: ON_UNWIND { block of code here };
92  *
93  * Defines a variable, with a generated name, to hold the unwinder.
94  */
95 #define ON_UNWIND unwinder CONCAT_TOK(_gensym_uw_, __LINE__) = [&] () -> void
96