Re: [RFA 02/22] Use RAII to save and restore scalars

Pedro Alves
2016-09-30T01:17:18Z

On 09/27/2016 09:59 AM, Trevor Saunders wrote:

>> I had hoped that template parameter deduction would work here, but it
>> did not, and so the patch uses explicit template parameters
>> everywhere.
>
> yeah, you can't deduce template args from a constructor in C++

I don't know whether the trick below is usual, but you can work
around that and end up with no-template-args-in-sight usage
like this:

void
foo ()
{
scoped_restore scope = make_scoped_restore (&some_global);

...
}

You'd do something like this:

struct scoped_restore_base {};

template<typename T>
class scoped_restore_ : public scoped_restore_base
{
public:
...
explicit scoped_restore (T *var)
: saved_var (var),
saved_value (*var)
{}

~scoped_restore ()
{
*saved_var = saved_value;
}

private:
scoped_restore &operator= (const scoped_restore &rhs);

// The saved variable.
T *saved_var;

// The saved value.
T saved_value;
};

typedef const scoped_restore_base &scoped_restore;

template<class T>
scoped_restore_<T> make_scoped_restore (T *var)
{
return scoped_restore_<T> (var);
}

The trick is making scoped_restore_ inherit a non-template
class, and make "scoped_restore" a typedef for a const reference
to the base class, since a const reference can bind to the
temporary that is returned by make_scoped_restore, and it
extends the temporary's lifetime.

While copy elision / RVO makes it so that the temporary returned by
value from make_scoped_restore is constructed directly in the caller
instead of constructed and then copied out, you still have to
provide the copy constructor, instead of "deleting" it as in your
original patch. Copy elision was optional until C++17, but I
believe all compilers worth their salt implement it. And if a compiler
doesn't implement it, it's not a real issue, anyway, the code
still works.

The downside is that printing a scoped_restore object from
gdb will just print the reference using the base type, so you
won't see stored fields.

(gdb) p scope
$1 = (const scoped_restore) @0x7fffffffd910: {<No data fields>}

To see the object's fields you'd have to downcast it manually, like:

(gdb) p (scoped_restore_<ui_out *>) scope
$1 = {<scoped_restore_base> = {<No data fields>}, saved_var = 0x601050 <current_uiout>, saved_value = 0x0}

Not sure whether that's an issue.

Thanks,
Pedro Alves