Microsoft is hardly known for their quality C++ code. Don't go looking there for inspiration.
Printable View
Microsoft is hardly known for their quality C++ code. Don't go looking there for inspiration.
In fact, in that particular piece of sample code one has to read no further than void main() ...
O_oQuote:
True, but I still see examples of goto for cleanup such as this one from MSDN which is C++ specific
You don't, but even if you did, do you want to write bad C++ just because someone else writes bad C++?
I should hardly think that would be fair to your own education.
What you see there is C90 with the "C++ Comments" extension; the code is also valid C99.
What it very definitely is not, is good C++; it doesn't even use any C++ features let alone best practices.
Microsoft's C++ examples aren't a good place to look for C++, but they aren't either a good place to find good C code.
Microsoft doesn't really care about C99; you'll find most of the "WinAPI" examples are using the version of C90 with extensions that the "Microsoft Visual C++" compiler also supports.
Soma
I don't, that was just an example of using goto to a common cleanup / exit point used in functions. This is a semi-standard at some of the companies I've worked for in the case of PC apps, although most of my work in the last decade has been with multi-threaded embedded systems (ARM cpu) using C (not C++), and most of the PC apps were test tools for the embedded systems (tape drives, hard drives, error correction code, compression, ... ).
Don't go looking to companies and most of the web for good C++ code, either. 99% of it is garbage.
Stick with this board, some well-known good books (C++ Primer, for example) and isocpp.org.
If you are going to write C++, then do it properly. Don't follow others simply because "they do it that way."
It's the reason C++ is in this trench and partly why it has such a bad reputation. You're not making it easier by fouling its reputation even more and producing more garbage into the world.
O_oQuote:
not C++
Indeed.
That's exactly the point.
Using `goto' for common cleanup in C is allowable if still awful.
There is no place for cleanup using `goto' in C++.
You can look for all the examples you want, but as we've said, just because other people write crap code, doesn't mean that you should write crap code.
You say this is a standard of yours from the past for another round; this attempt to defend bad code doesn't do anything for us. You see, the same thing applies to your own past: just because you have written crap code doesn't mean that you should continue to write crap code.
Soma
O_oQuote:
Unless its a corporate standard at the company you work for, mostly to cover the stuff not handled by exceptions sometimes combined with the single exit (return) point rule.
You are under the false impression the existence of a corporate standard changes reality.
If you are using C++ mechanisms which may raise an exception, you can't use `goto' for cleanup. I'm not saying that you simply "shouldn't" because I don't like to see `goto'. I'm saying that you can't use `goto' for cleanup in the face of exceptions without dealing specifically with the exceptions; it is impossible.
If you are using mechanisms which may raise exceptions and also have some mechanisms which reports errors without exceptions, like POSIX functions for example, which require cleanup code you can't use `goto' for cleanup. I'll make this simple: `throw' trumps `goto'. You will have to use "RAII" and exception constructs to cleanup resources in the face of exceptions even for resources managed with interfaces which don't raise exceptions precisely because an exception may be raised which bypasses your `goto' yet you want/need/must still cleanup those resources managed by other API.
Also, you can't have a single point of return if you are using constructs which raise exceptions, the point of this side discussion, without locally catching every exception, which completely eliminates the reason exceptions exist so is pointless to discuss, or using "RAII" constructs. The exception itself is a point of return for any statement which uses exceptions to signal an error; you can't get past an exception with `goto'; the only option is, again, wrapping every exception locally, which again completely eliminates the reason exceptions exist.
Soma
I don't think rcgldr claims that SESE is a great idea. 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_oQuote:
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.
I didn't speculate on whether or not "SESE" was a dumb idea.
I stated a fact about reality.
1): This code is not good C++.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);
}
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.)
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);
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 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(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, 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.)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);
}
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
The SESE part of this discussion should get moved to another thread. antred is correct, it's a corporate standard at some places I worked at. In some cases, you're supposed to avoid stuff that can cause exceptions, which means some of C++'s features never get used on these projects.