I wrote a scope guard which resets a value when the scope exits:
template <class T>
struct ResetGuard
{
T old_value;
T& obj_to_reset;
ResetGuard(T& obj_to_reset, const T& new_value) :
old_value(obj_to_reset),
obj_to_reset(obj_to_reset)
{
obj_to_reset = new_value;
}
~ResetGuard() { obj_to_reset = old_value; }
};
When this scope guard is returned from a function, is there any way to prevent the immediate destruction of the scope guard if it wasn't saved?
For example:
int GLOBAL_VALUE = 0;
ResetGuard<int> temporarily_set_global_value(int new_val) {
return { GLOBAL_VALUE, new_val }; //updates the global variable
}
void foo() {
//Ideally, someone calling this function
//Wouldn't have to save the returned value to a local variable
temporarily_set_global_value(15);
std::cout << "GLOBAL_VALUE is " << GLOBAL_VALUE << std::endl;
}
The way it's written now, anyone calling one of the functions would have to remember to always save the ResetGuard to a local variable, otherwise it would immediately reset the value.
Some context around what I'm trying to do
I'm writing a library to format and manipulate strings. I have one global variable controlling how floating point numbers are formatted. I know that global variables are typically a terrible idea, but please bear with me.
I made the decision to use a global variable carefully. The alternative to using a global variable would be to pass around the object containing the formatting specification. This option ultimately proved infeasible: my library is designed to work with any objects that provide a implicit conversion to std::string
. There's no way to pass formatting options (or any parameters, really) to an implicit conversion function. Hence, I had to resort to using a global variable.
Sorry about my previous answer folks, what was I thinking? I should have read the question properly.
So, of course,
foo()
has to return yourResetGuard
object in order to extend its lifetime, and this is a good thing, not a bad thing.Firstly, it's hardly a burden on the caller. After all, all he / she has to do is:
As a potential caller of
foo()
I would have absolutely no problem with that, and @melpomene's excellent suggestion in the comments above ([[nodiscard]]
) can be used to ensure that callers don't forget to do this.And why is forcing the caller to do this a good thing (apart from the fact that you have no choice in the matter anyway)? Well, it gives the caller the opportunity to manage the lifetime of the scopeguard and that might be useful (will provide live demo soon).
As for the other answers here, I would most definitely not hide all this in a macro because that hides an important piece of information from potential callers of
foo()
. Instead, I would use[[nodiscard]]
to remind them of their responsibilities and leave it at that.[Edit]
I have now spent a little time over at Wandbox polishing up the code to add the full set of recommended constructors / assignment operators and to demonstrate the use of
[[nodiscard]]
, which for me is the find of the day.First, the modified class, done in the way (I believe) those who know are recommending. I can particularly see the importance of defining a proper move constructor (just think of the subtle bugs you might run into if you don't). Pinched some stuff (
= delete
) from JVApen, looks wise to me, TU JV.Comment out
#define INCLUDE_COPY_MOVE_SWAP_STUFF
to see the compiler warning you get if you don't do all the things you're supposed to.Test program:
Compiler output:
Program output:
So there you have it. If you do all the things you're supposed to do (and I hope I have!) then everything works just fine, and it's all nice and efficient thanks to RVO and guaranteed copy elision so there's no need to worry about that either.
Live demo.