Reputation: 51465
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
Upvotes: 9
Views: 1285
Reputation: 247949
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
}
Upvotes: 1
Reputation: 9866
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(...);
}
}
Upvotes: 1
Reputation: 7010
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
Upvotes: 1
Reputation: 503835
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.
Upvotes: 4
Reputation: 40272
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
Upvotes: 2
Reputation: 279255
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
Upvotes: 4
Reputation: 7127
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
Upvotes: 4
Reputation: 355049
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
}
Upvotes: 7
Reputation: 81149
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.
Upvotes: 2
Reputation:
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.
Upvotes: 8
Reputation: 6948
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: ...
}
Upvotes: 1
Reputation: 30394
if (data.device) {
bool ok = true;
try {
...
}
catch(std::exception) { ok = false; }
if(ok) {
... // more things which should not be caught
}
}
Upvotes: 3
Reputation: 19044
if (data.device)
{
bool status = true;
try
{
...
}
catch(std::exception)
{
status = false;
}
if (status)
{
... // more things which should not be caught
}
}
Upvotes: 15