Как провести рефакторинг этого GOTO (без освобождения ресурсов)?

Это простой вопрос GOTO потока управления, ничего не о распределении ресурсов.

Существует два уровня проверки "хороших" данных. Если и только если он проходит первую проверку, мы выполняем вторую проверку. Если данные не проходят ни один из тестов, вместо этого мы используем значение по умолчанию.

Вторая проверка включает в себя несколько шагов с промежуточными данными, поэтому мы не можем просто поместить ее в состояние короткого замыкания ||. Кроме того, если второй тест пройден, мы используем выходные данные второго теста вместо исходных данных.

Это внутренний цикл обработки в реальном времени, поэтому эффективность очень важна. Мы не хотим делать какие-либо вычисления более одного раза.

if (firstCheck(data)) {
    result = analyze(data);
    if (secondCheck(result)) {
        use_result(result);
    }
    else {
        goto FAIL;
    }
}
else {
FAIL:
    use_result(DEFAULT_VALUE);
}

Этот GOTO, кажется, удовлетворяет всем моим требованиям с максимальной эффективностью. Я могу придумать другие способы сделать это, но все они потребуют дополнительного хранилища или условных обозначений. Однако я с осторожностью отношусь к GOTO. Фактически, если я воспользуюсь этим, это будет первый раз, когда я использую GOTO. Так что, пожалуйста, помогите мне найти выход!


person japreiss    schedule 07.11.2011    source источник


Ответы (3)


Используйте continue для перехода к следующей итерации цикла

if (firstCheck(data)) {
    result = analyze(data);
    if (secondCheck(result)) {
        use_result(result);
        continue;
    }
}
use_result(DEFAULT_VALUE);
person gustavotkg    schedule 07.11.2011
comment
Интересно, что в обоих ответах используются уловки, чтобы заставить его работать: continue в основном ограниченный goto, а назначение C - это выражение идиома, провоцирующая взлом языка IMO. Принимаю это, потому что это дает мне больше гибкости с analyze() и secondCheck(), но голосование за оба. - person japreiss; 08.11.2011

Что ж, мы можем исправить это без особых усилий, но использование goto таким образом не обязательно является плохим поведением - например, ядро linux использует это соглашение для обработки ошибок только в одном месте, и я думаю, что код довольно ясен как есть.

Использование исключений для исправления этого, очевидно, является одним из решений, но это снижает производительность в случае ошибки, поэтому я полагаю, что об этом не может быть и речи.

Так что, если хотите, все должно быть в порядке:

if (firstCheck(data) && secondCheck(result = analyze(data)) {
    use_result(result);
}
else {
    // fail
}
person Voo    schedule 07.11.2011
comment
use_result(result) выйдет из строя, потому что результат не определен. Кроме того, OP не хочет анализировать более одного раза. - person gustavotkg; 07.11.2011
comment
О, ты прав, лучше исправь это. Я не вижу, где мы анализируем больше одного раза ... - person Voo; 08.11.2011
comment
Теперь вы не анализируете больше одного раза, но я боялся use_result(analyze(data)), который не использовался в вашем текущем коде. - person gustavotkg; 08.11.2011
comment
Где? У меня есть только один вызов для анализа, и последовательность вызовов эквивалентна заданной: firstCheck, [analysis, secondCheck], если firstCheck истинно, [use_result], если secondCheck истинно. Хорошо, мы должны использовать функцию для второго блока кода, если он больше одного оператора, но со встроенной функцией, которая не теряет. - person Voo; 08.11.2011
comment
Вы можете уменьшить влияние исключений на производительность либо путем создания подкласса исключения, который предотвращает заполнение его трассировки стека, либо путем создания объекта исключения только один раз, его кэширования и последующего многократного выброса кэшированного исключения. В случае, когда вы используете исключение только для управления потоком, вам не важна трассировка стека. - person Matthew Cline; 08.11.2011
comment
@MatthewCline интересная идея, что. Я помню, что мне пришлось переписать какое-то исключение, используя код, потому что он был недостаточно производительным в жестком цикле (с кешированием объекта исключения), но подклассифицировал исключение, чтобы избежать трассировки стека? Не знаю, насколько компилятор мог бы оптимизировать в таком случае, но мне придется когда-нибудь попробовать. - person Voo; 08.11.2011

Вы не просто используете goto. Вы используете goto, который переходит в другую область видимости. Гораздо проще:

if (firstCheck(data)) {
    result = analyze(data);
    use_result (secondCheck (result) ? result : DEFAULT_VALUE);
}
else {
    use_result(DEFAULT_VALUE);
}

Более чистый, без дублирования кода и масштабируемый:

int result = DEFAULT_VALUE;
if (firstCheck(data)) {
    int tmpResult = analyze(data);
    if (secondCheck (tmpResult))
        result = tmpResult;
}
use_result (result);
person gnasher729    schedule 25.02.2015