Я изменил заголовок немного, потому что я думал, что это - более соответствующий вопрос.
Вы осуществили бы рефакторинг его (походит на законное использование goto)? Если, как Вы осуществили бы рефакторинг следующий код для удаления, переходят к оператору?
if (data.device) {
try {
...
}
catch(const std::exception&) { goto done; }
... // more things which should not be caught
done: ;
}
законченное высказывание
#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
спасибо
после видели предпочтение большинства, я решил пойти с флагом, но с комментарием г-на York.
Поблагодарите Вас все
if (data.device)
{
bool status = true;
try
{
...
}
catch(std::exception)
{
status = false;
}
if (status)
{
... // more things which should not be caught
}
}
А как насчет простого использования флага и добавления условного оператора?
int caught = 0;
if (data.device) {
try {
/* ... */
} catch (std::exception e) { caught = 1; }
if (!caught) {
// more stuff here
}
// done: ...
}
Просто разбейте ее на отдельную функцию / метод (включая все, что находится после нее) и используйте return
ключевое слово. Пока все ваши переменные имеют деструкторы, выделены стеком (или имеют интеллектуальные указатели, если это неизбежно), тогда все в порядке. Я большой поклонник функций / методов раннего выхода, а не постоянной проверки флагов.
например:
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();
}
Таким образом, сложно ошибиться
Думаю, вам подойдет вариант.
// 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
Если бы вы могли отредактировать библиотеку графического процессора и присвоить всем исключениям графического процессора некоторую основу, например gpu_exception
, код стал бы намного проще:
// 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
Если это не работает, я думаю, что следующий лучший вариант - ] Ответ Стива .
Немного другое использование флага. Думаю, он опрятнее, чем у Амардипа.
Я бы предпочел использовать флаг, чтобы указать, следует ли распространять исключение, чем флаг, чтобы указать, сработало ли последнее действие, потому что вся точка исключений предназначена для того, чтобы избежать проверок того, было ли последнее событие работает - идея состоит в том, чтобы написать такой код, чтобы, если мы зашли так далеко, все работало, и мы могли продолжить.
#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
Почему бы не переместить дополнительный код внутрь блока try?:
#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
Во-первых: goto само по себе не является злом. Рефакторинг только ради того, чтобы в коде не было буквы "goto" - это нонсенс. Рефакторинг на что-то, что делает то же самое чище, чем goto, - это нормально. Изменение плохого дизайна на тот, который лучше и не нуждается в goto или его замене - тоже нормально.
Учитывая это, я бы сказал, что ваш код выглядит именно так, как и было задумано. Жаль, что в C++ нет ничего подобного... так что, возможно, самое простое решение - оставить все как есть.
Я что-то упустил, или это было бы эквивалентно перемещению части между меткой catch
и done:
внутри блока try
?
#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
Причина, по которой goto сегодня не одобряется, заключается в том, что вместо него у нас есть эти причудливые вещи, называемые "функциями". Заверните код GPU в его собственную функцию, которая может вернуться раньше времени в случае неудачи.
Затем вызовите ее из вашей исходной функции.
Поскольку им, вероятно, придется совместно использовать несколько переменных (data
, host_index
и block_index
, похоже), поместите их в класс и сделайте две функции его членами.
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
}
catch(std::exception) { return; }
Это должно помочь. Я, конечно, предполагаю, что done
действительно находится в конце функции.
Если вам нужно выполнить дополнительный код при возникновении исключения, верните статус или вызовите исключение, которое находится на более подходящем уровне абстракции (см. Ответ Джеймса).
Я представляю себе что-то вроде:
doStuff(...) {
bool gpuSuccess = doGPUStuff(...);
if (!gpuSuccess) {
doCPUStuff(...);
}
}
Вы можете перехватить исключение и повторно вызвать конкретное исключение, которое может обрабатываться вне условного блока.
// 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
}
if (data.device) {
bool ok = true;
try {
...
}
catch(std::exception) { ok = false; }
if(ok) {
... // more things which should not be caught
}
}
Когда есть связка кода, которая должна завершиться по какому-либо условию, я предпочитаю использовать цикл «do {} while (0)» и «break», когда это необходимо. Я не знаю, что сломать; все же сделаю в улове. «Goto» может быть вашим лучшим выбором, если «break» не сработает.