When I have a method that calls a set of methods that offer strong guarantee, I often have a problem on rolling back changes in order to also have a strong guarantee method too. Let’s use an example:
// Would like this to offer strong guarantee
void MacroMethod() throw(...)
{
int i = 0;
try
{
for(i = 0; i < 100; ++i)
SetMethod(i); // this might throw
}
catch(const std::exception& _e)
{
// Undo changes that were done
for(int j = i; j >= 0; --j)
UnsetMethod(j); // this might throw
throw;
}
}
// Offers strong guarantee
void SetMethod(int i) throw(...)
{
// Does a change on member i
}
// Offers strong guarantee
void UnsetMethod() throw(...)
{
// Undoes a change on member i
}
Obviously, the UnsetMethod could throw. In which case, my MacroMathod() only offers basic guarantee. Yet, i did all I could to offer a strong guarantee, but I can’t be absolutly sure my UnsetMethod() will not throw. Here’s my questions:
- Should I even try to offer a strong guarantee in this case?
- Should I document my MacroMethod() as having a basic or strong guarantee? Even if it is very unlikely UnsetMethod will throw?
- Can you see a way to make this method truly offer a strong guarantee?
- I should probably put the call to UnsetMethod() in a try, but that feels rather heavy, and what should I do in the catch?
Thanks!
A good pattern to try to achieve this is to make your method work on a copy of the object that you want to modify. When all modifications are done, you swap the objects (swap should be guaranteed not to throw). This only makes sense if copy and swap can be implemented efficiently.
This method has the advantage that you do not need any
try...catch-blocks in your code, and also no cleanup-code. If an exception is thrown, the modified copy gets discarded during stack unwinding, and the original was not modified at all.