So we're investigating the use of scope guards or some similar mechanism to ensure incoming/outgoing object validity and/or internal state invariance, similar to C# Code Contracts.
In the specific case where an unexpected condition/exception arises in the middle of normal processing that causes some objects to be left in an inconsistent state, what mechanism could/should we use to side-step the fact that the scope guard is going to complain when we jump out of the function?
Here's some sample pseudocode to illustrate my point:
struct IObjectValidator;
struct ObjectValidatorScopeGuard
{
ObjectValidatorScopeGuard(IObjectValidator * pObj)
: m_ptr(pObj)
{
Assert(!m_ptr || m_ptr->isValid());
}
~ObjectValidatorScopeGuard()
{
Assert(!m_ptr || m_ptr->isValid());
}
private:
IObjectValidtor * m_ptr;
};
int SomeComponent::CriticalMethod(const ThingA& in, ThingB& inout, ThingC * out)
{
ObjectValidatorScopeGuard sg1(static_cast<IObjectValidator *>(&in));
ObjectValidatorScopeGuard sg2(static_cast<IObjectValidator *>(&inout));
ObjectValidatorScopeGuard sg3(static_cast<IObjectValidator *>(out));
// create out
try
{
out = new ThingC();
out->mergeFrom(inout, out); // (1)
}
catch (const EverythingHasGoneHorriblyWrongException& ex)
{
// (2) out and inout not guaranteed valid here..
}
return 0;
}
So if something goes wrong in (1) that causes 'out' or 'inout' to be in a bad state at point (2), the scope guards sg2/sg3 are going to throw exceptions... and those exceptions could mask the true cause.
Is there any pattern/convention to work with this scenario? Are we missing something obvious?
In case of an exception in the block of code guarded by your object validator, the C++ runtime will call
terminate. You cannot throw exceptions, as your destructor does, while other exception is being handled. Therefore you should not throw exceptions from destructor (details here). Instead of throwing an exception you should use assert or log the error.Still better than checking invariants is to guarantee that they will never be broken. That is called exception safety. Basic exception safety (preserving the invariants) is usually easy to achieve just by clever reordering of statements and using RAII.
Example of exception safety techniques: