Ok, the question of the century :)
Before you say or think anything, let me tell you that I've read couple of similar questions about this very topic, but I didn't find clear solution for my problem. My case is specific, and typical for system programmers I think.
I have this situation very often. I hate gotos, don't really know why, probably because everybody is yelling that it's bad. But until now I did not find better solution for my specific scenario, and the way I do it currently might be uglier than the use of goto.
Here is my case: I use C++ (Visual C++) for Windows application development, and quite often I use bunch of APIs in my routines. Suppose following situation:
int MyMemberFunction()
{
// Some code... //
if (!SomeApi())
{
// Cleanup code... //
return -1;
}
// Some code... //
if (!SomeOtherApi())
{
// Cleanup code... //
return -2;
}
// Some more code... //
if (!AnotherApi())
{
// Cleanup code... //
return -3;
}
// More code here... //
return 0; // Success
}
So after each Api I have to check if it succeeded, and abort my function if it did not. For this, I use whole bunch of // Cleanup code... //
, often pretty much duplicated, followed by the return
statement. The function performs, say, 10 tasks (e.g. uses 10 Apis), and if task #6 fails, I have to clean up the resources created by previous tasks. Note that the cleanup should be done by the function itself, so exception handling cannot be used. Also, I can't see how much-talked RAII can help me in this situation.
The only way I've thought of, is to use goto to make jump from all such failure cases to one cleanup label, placed at the end of the function.
Is there any better way of doing this? Will using goto considered bad practice in such situation? What to do then? Such situation is very typical for me (and for system programmers like me, I believe).
P.S.: Resources that need to be cleanup up, are of different types. There might be memory allocations, various system object handles that need closure, etc.
UPDATE:
I think people still did not get what I wanted (probably I'm explaining badly). I thought the pseudo-code should be enough, but here is the practical example:
I open two files with CreateFile. If this step fails: I have to cleanup the already-open files handle(s), if any. I will later read part of one file and write into another.
I use SetFilePointer to position read pointer in first file. If this step fails: I have to close handles opened by previous step.
I use GetFileSize to get target file size. If api fails, or file size is abnormal, I have to make cleanup: same as in previous step.
I allocate buffer of specified size to read from first file. If memory allocation fails, I have to close file handles again.
I have to use ReadFile to read from first file. If this fails, I have to: release buffer memory, and close file handles.
I use SetFilePointer to position write pointer in second file. If this fails, same cleanup has to be done.
I have to use WriteFile to write to second file. If this fails, bla-bla-bla...
Additionally, suppose I guard this function with critical section, and after I call EnterCriticalSection
in the beginning of the function, I have to call LeaveCriticalSection
before every return
statement.
Now note that this is very simplified example. There might be more resources, and more cleanup to be done - mostly same, but sometimes a little bit different, based on which step did fail. But let's talk within this example: how can I use RAII here?