The fact of the matter is that if the coding guidelines of your project demand single-entry-single-exit, you have no choice, no matter how dumb an idea it is.
O_o
I didn't speculate on whether or not "SESE" was a dumb idea.
I stated a fact about reality.
Code:
Resource3 * DoSomething()
{
Resource1 * s1(0);
Resource2 * s2(0);
Resource3 * s3(0);
try
{
s1 = GetUseResource1();
}
catch(...)
{
goto cleanup1;
}
try
{
s2 = GetUseResource2();
}
catch(...)
{
goto cleanup2;
}
try
{
s3 = GetUseResource3(s1, s2);
}
catch(...)
{
goto cleanup3;
}
// Use `s3' to do something.
FreeResource3(s3);
cleanup3:
FreeResource3(s2);
cleanup2:
FreeResource1(s1);
cleanup1:
return(s3);
}
1): This code is not good C++.
2): This code violates the very nature of exceptions.
3): The code completely eliminates the reason to use exceptions to report the error condition.
4): To get "SESE", I've had to locally `catch' every exception and otherwise ignore it.
The idea of "SESE" isn't necessarily a bad one, and I didn't say anything of the sort, but I have had to violate "best practices" and canonical standards to get it, and that is the point of my comments about "SESE": you still have to deal with the exceptions to get "SESE" and `goto' style cleanup. (Granted, you don't have to do it as I've done it for the sake of example; I'd actually recommend using "RAII" based facilities as Elysia and I've referenced, but to get "SESE" even with "RAII" cleanup you'd still have to deal with the exceptions by wrapping the "RAII" variable declarations and all related code in a `try' so that you could swallow the exception and return normally from the single return point.) Well, that was my point, but I also say that in dealing with the exceptions just to get "SESE" you are necessarily misusing exceptions by mindlessly swallowing all exceptions.
Now, I know what you are thinking, why multiple points of interest? Who said that `FreeResource3' was clean in the face of an invalid object? You want trade code wrapping the functions to behave safely in the face of an invalid object to simplify the cleanup? (The `FreeResource3' could be designed to raise an exception during debugging and simply fail in a release build if the contract is violated.)
Code:
Resource3 * DoSomething()
{
Resource1 * s1(0);
Resource2 * s2(0);
Resource3 * s3(0);
try
{
s1 = GetUseResource1();
}
catch(...)
{
goto cleanup;
}
try
{
s2 = GetUseResource2();
}
catch(...)
{
goto cleanup;
}
try
{
s3 = GetUseResource3(s1, s2);
}
catch(...)
{
goto cleanup;
}
// Use `s3' to do something.
cleanup:
MaybeFreeResource3(s3);
MaybeFreeResource3(s2);
MaybeFreeResource1(s1);
return(s3);
}
Well, that's done, and it looks better, but you could, obviously, wrap the individual `GetUseResource1' functions behind similar functions which fail with an error value instead of an exception, and you'd still have to wrap any other expression that may raise an exception, but yes, you could transform the exception into an error code with a wrapper function.
Code:
Resource3 * DoSomething()
{
Resource1 * s1(0);
Resource2 * s2(0);
Resource3 * s3(0);
if(TryGetUseResource1(&s1)) == INVALID)
{
goto cleanup;
}
if(TryGetUseResource2(&s2)) == INVALID)
{
goto cleanup;
}
if(TryGetUseResource3(s1, s2, &s3)) == INVALID)
{
goto cleanup;
}
// Use `s3' to do something.
cleanup:
MaybeFreeResource3(s3);
MaybeFreeResource3(s2);
MaybeFreeResource1(s1);
return(s3);
}
Well, that was easy. Sure, we've wrapped six functions, but they were easy to wrap. However, while we are changing the meaning of the functions, why not change the meaning enough we can't count on acquisition in a similar way by still returning the newly allocated resource instead of a specific error/success code? (We are using a pointer which has an available error state versus possible valid states.)
Code:
Resource3 * DoSomething()
{
Resource1 * s1 = 0;
Resource2 * s2 = 0;
Resource3 * s3 = 0;
if
(
(s1 = TryGetUseResource1())
&& (s2 = TryGetUseResource2())
&& (s3 = TryGetUseResource3(s1, s2))
)
{
// Use `s3' to do something.
}
MaybeFreeResource1(s1);
MaybeFreeResource2(s2);
MaybeFreeResource3(s3);
return(s3);
}
Well, we've come along way to get "SESE" in C++, and we've had to do exactly what I said would be necessary, but I suppose the important thing is that we have "SESE". (No; getting "SESE" isn't important; this was a joke. If you have a standard that mandates "SESE" in C++ while allowing the use of mechanisms that raise exceptions the first step is changing policy so you don't have to do this nonsense one way or the other.)
The question is, if you are going to wrap every C++ function using exceptions to translate exceptions into error code or similar why didn't you start with good C practices in the first place? More to the point, why did you start with functions that used exceptions to signal errors if you are required to follow such an idiotic standard? (The idiotic standard is that of being allowed to mix exceptions, error codes, `goto' cleanup, and unowned resources in the same bit of code.)
No, I'm not going to believe that any coding standard exists that suggest someone write code in the face of exceptions only to have you wrap every problematic line of code to get "SESE".
Sure, some standards do require "SESE", but you shouldn't be using C++ facilities which raise exceptions in the first place if you require "SESE", and that was the motivating case for the response: using `goto' for cleanup in the face of exceptions. If we had simply used facilities that didn't raise exceptions in the first place, we wouldn't have had to wrap six functions, yet we could have written the best "SESE" code in exactly the same way.
Soma