views:

586

answers:

14

hello

I have changed title slightly because I thought this is more appropriate question.

Would you refactor it (seems like legitimate use of goto) ? If, how would you refactor the following code to remove go to statement?

if (data.device) {
    try {
        ...
    }
    catch(const std::exception&) { goto done; }
    ... // more things which should not be caught
done: ;
}

complete statement

#ifdef HAVE_GPU
            // attempt to use GPU device
            if (data.device) {
                try {
                    Integral::Gpu eri(S, R, Q, block.shell());
                    eri(basis.centers(), quartets, data.device);
                }
                // if GPU fails, propagate to cpu
                catch(std::exception) { goto done; }
                data.device += size;
                host_index.extend(block_index);
                block_index.data.clear();
            done: ;
            }
#endif

thank you

after have seen preference of most, I decided to go with flag, but with Mr. York comment.

Thank you everybody

+2  A: 

Can you not catch the exception outside the if?

djna
other logic would make it very ugly, so I would rather not
aaa
+14  A: 
if (data.device)
{
    bool status = true;

    try
    {
        ...
    }
    catch(std::exception)
    {
        status = false;
    }

    if (status)
    {
    ... // more things which should not be caught
    }
}
Amardeep
may be somewhat obvious answer, but which would you prefer? Status or goto?
aaa
@aaa: I may not be the best person to ask since I'm an unrepenting antigotoite. :-) Earlier today I was downvoted for answering a question with "Here's how you'd do it without goto: ..." But my reasons have more to do with scalability and orthogonality than just a dislike for the keyword.
Amardeep
Why do people insist on introducing new variables and branches just to avoid a goto? Stop drinking the kool aid of "Considered Harmful" papers (better yet, actually read the paper and you'll see Dijkstra doesn't save never use them) and actually put for some critical thinking about what you're doing. You've added more state and more branches to your code just to satisfy "I didn't use `goto`."
Nathan Ernst
Rather than set status = true. I would set status = false as the initial state. Then as the last statment inside the true set status = true thus it is false unless it makes it all the way through the try block without thorwing.
Martin York
+3  A: 
if (data.device) {
    bool ok = true;
    try {
        ...
    }
    catch(std::exception) { ok = false; }

    if(ok) {
        ... // more things which should not be caught
    }
}
Scott Saunders
Same answer as shown above in one of the previous answers.
C Johnson
+6  A: 

You can catch the exception and rethrow a specific exception that can be handled outside of the conditional block.

// obviously you would want to name this appropriately...
struct specific_exception : std::exception { };

try {
    if (data.device) {
        try {
            // ...
        }
        catch(const std::exception&) { 
            throw specific_exception(); 
        }

        // ... more things which should not be caught ...
    }
}
catch (const specific_exception&) { 
    // handle exception
}
James McNellis
thank you. I have thought about it but it adds more complexity in my opinion than goto/flag
aaa
@aaa: This is, in my opinion, far cleaner than either of those options. A `goto` is entirely unwarranted in this case, and if you use a status flag you end up mixing two different failure reporting mechanisms (status flags and exceptions) in a single block of code, which greatly increases complexity and makes it more difficult to reason about the code.
James McNellis
@James McNellis: What is the meaning of *goto is warranted/unwarranted*? The behavior of `goto` is well defined and (I believe) will do what you would expect, or will it not? I agree that adding a bool check is *not* the way to go anyway.
David Rodríguez - dribeas
@David: Well, I was subconsciously using the OP's word "unwarranted" to express that I wouldn't use `goto` in this scenario. While the `goto` would probably work in this scenario (I have to admit, I've not written a `goto` in C++ code, ever), I don't think a nested try/catch block adds much complexity and it makes the intent of the code clearer (because there is exactly one error handling mechanism being used--exceptions).
James McNellis
I think is far worse than the original use of goto. The status answer above is so much cleaner and simpler.
C Johnson
+1  A: 

What about just using some flag and adding a conditional statement?

int caught = 0;
if (data.device) {
    try {
        /* ... */
    } catch (std::exception e) { caught = 1; }
    if (!caught) {
        // more stuff here
    }
    // done: ...
}
tjko
Do you have something against the `bool` type?
Fred Larson
+7  A: 

First: goto isn't evil per se. Refactoring for the pure sake of not having the letters "goto" in your code is nonsense. Refactoring it to something which gets the same thing done cleaner than a goto is fine. Changing a bad design into one which is better and doesn't need a goto or a replacement for it is fine, too.

That being said, I'd say your code looks exactly like what finally was invented for. Too sad C++ doesn't have something like this... so maybe the easiest solution is to leave it like that.

delnan
Hell yeah! Go goto! go go go! to!
Gianni
Unfortunately finally() is not the correct solution here as the code is only used when there is no exception, finally on the other hand means that it should always be executed (even when exceptions are present). Though finally is required for languages like C# and Java it is not required for C++ as there are better techniques for implementing the same thing. Though I agree with all your other points. Don't fix what is not broken.
Martin York
Up vote for proper use and spelling of 'per se'.
Shawn D.
@Martin York is right. That part was nonesense, now I wonder how I got that idea o.O@sdeer: Wow, that's a... interesting reason for +1, but considered the grammar and wording of some fellas, I can understand it.
delnan
+2  A: 

When there is a bunch of code that needs to exit based on some condition, my preferred construct is to use a "do {} while(0)" loop and "break" when appropriate. I don't know what break; will do in a catch, though. A "goto" might be your best bet if "break" won't work.

supercat
That is worse than a `goto` in every way.
Thomas
+1 That is just as bad as the `goto`, but better than adding an extra variable and having to add conditions in the code. And I am inclined to think that either of these are better than throwing a new exception type... I still prefer the `goto`, but heck this answer deserves no less than the others :)
David Rodríguez - dribeas
+1  A: 
catch(std::exception) { return; }

That should do the trick. I am, of course, assuming that done is indeed at the end of a function.

If you need to execute additional code when the exception occurs, return a status or throw an exception that is at a more proper level of abstraction (see James' answer).

I'm imagining something like:

doStuff(...) {
    bool gpuSuccess = doGPUStuff(...);
    if (!gpuSuccess) {
        doCPUStuff(...);
    }
}
Justin Ardini
And skips everything else in the function.
GMan
Which may well be the right thing to do, or at least to refactor to.
David Thornley
I don't think `done:` is currently the end of a function - it's followed by the code to use the CPU rather than the GPU (which must either have failed or been compiled out).
Steve Jessop
@Steve: Probably true. I'd likely use more than one function for this, otherwise follow James McNellis' approach.
Justin Ardini
+4  A: 

Why not move the extra code inside the try block?:

#ifdef HAVE_GPU
            // attempt to use GPU device
            if (data.device) {
                try {
                    Integral::Gpu eri(S, R, Q, block.shell());
                    eri(basis.centers(), quartets, data.device);
                    data.device += size;
                    host_index.extend(block_index);
                    block_index.data.clear();
                }
                // if GPU fails, propagate to cpu
                catch(std::exception) { /* handle your exception */; }
            }
#endif
Jeff Paquette
OP's code made clear, that the first two lines contain a rated break point which is allowed to throw. Your example would swallow every exception. `host_index.extend()` is now allowed to fail silently.
Luther Blissett
Basically your saying, put the minimal amount of code inside of a try block as possible?
C Johnson
If he made that clear, it wasn't in the question. Note the comment "handle your exception" within the catch. It's up to the OP to write that code.
Jeff Paquette
+4  A: 

I think a variant of this might work for you.

// attempt to use GPU device
if (data.device)
{
    try
    {
        Integral::Gpu eri(S, R, Q, block.shell());
        eri(basis.centers(), quartets, data.device);

        data.device += size;
        host_index.extend(block_index);
        block_index.data.clear();
    }
    catch (const std::bad_alloc&)
    {
        // this failure was not because 
        // of the GPU, let it propagate
        throw;
    }
    catch(...)
    {
        // handle any other exceptions by
        // knowing it was the GPU and we 
        // can fall back onto the CPU.
    }
}

// do CPU

If you could edit the GPU library and give all GPU exceptions some base like gpu_exception, the code becomes much simpler:

// attempt to use GPU device
if (data.device)
{
    try
    {
        Integral::Gpu eri(S, R, Q, block.shell());
        eri(basis.centers(), quartets, data.device);

        data.device += size;
        host_index.extend(block_index);
        block_index.data.clear();
    }
    catch (const gpu_exception&)
    {
        // handle GPU exceptions by
        // doing nothing and falling
        // back onto the CPU.
    }

    // all other exceptions, not being 
    // GPU caused, may propagate normally
}

// do CPU

If nether of these work, I think the next best thing is Steve's answer.

GMan
+4  A: 

Slightly different use of a flag. I think it's neater than Amardeep's.

I'd rather use a flag to indicate whether to propagate the exception, than a flag to indicate whether the last thing worked, because the entire point of exceptions is to avoid checks for whether the last thing worked -- the idea is to write code such that if we got this far, then everything worked and we're good to continue.

#ifdef HAVE_GPU
    // attempt to use GPU device
    if (data.device) {
        bool dont_catch = false;
        try {
            ...
            dont_catch = true;
            ... // more things which should not be caught
        } catch (...) {
            if (dont_catch) throw;
        }
    }
#endif
Steve Jessop
Oh, good thinking. :)
GMan
@GMan: change made - it's different behaviour to the questioner's code, of course, but it saves an argument over value vs reference ;-)
Steve Jessop
+3  A: 

Am I missing something, or wouldn't it be equivalent to move the part between the catch and done: label inside the try block?

#ifdef HAVE_GPU
            // attempt to use GPU device
            if (data.device) {
                try {
                    Integral::Gpu eri(S, R, Q, block.shell());
                    eri(basis.centers(), quartets, data.device);
                    data.device += size;
                    host_index.extend(block_index);
                    block_index.data.clear();
                }
                // if GPU fails, propagate to cpu
                catch(std::exception) {}
            }
#endif
Nick Meyer
+1: *If* this is what the OP wants to do, it's the simplest solution.
Justin Ardini
Yes, you are missing something. Now if anything that was outside the try before throws, its exception just gets eaten.
GMan
+1  A: 

Just break it out into its own function/method (including everything after it) and use the return keyword. As long as all your variables have destructors, are stack-allocated (or smart-pointered if unavoidable), then you're fine. I'm a big fan of early-exit functions/methods rather than continual flag-checking.

for example:

void myFunc()
{
    String mystr("heya");
    try
    {
        couldThrow(mystr);
    }
    catch(MyException& ex)
    {
        return; // String mystr is freed upon returning
    }

    // Only execute here if no exceptions above
    doStuff();
}

This way, it's hard to go wrong

Kevin
Agreed. Just put it in a function, and return when you want to, well, return.
jalf
+1  A: 

The reason goto is frowned on today is that we have these fancy things called "functions" instead. Wrap the GPU code in its own function, which can return early if it fails.

Then call that from your original function.

Since they'll probably need to share a few variables (data, host_index and block_index, it looks like), put them in a class, and make the two functions members of it.

void RunOnGpu(){
        // attempt to use GPU device
        if (data.device) {
            try {
                Integral::Gpu eri(S, R, Q, block.shell());
                eri(basis.centers(), quartets, data.device);
            }
            // if GPU fails, propagate to cpu
            catch(std::exception) { return; }
            data.device += size;
            host_index.extend(block_index);
            block_index.data.clear();     
}
void DoStuff() {
#ifdef HAVE_GPU
    RunOnGpu();
#endif
}
jalf