LINUX.ORG.RU

Как предполагают одну точку выхода?

 , ,


1

2

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

А скажите, господа, а как предполагается тогда писать такие функции? Через миллионы вложенных if'ов? А как это поможет читабельности и надежности?

               }
              }
             }
            }
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }
 }
}
См. https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming)

Собственно код, на котором мне бомбануло:

static YGGBool compare_records_str(CML_Node * haystack1, CML_Node * haystack2, char * needle)
{
    CML_Node * finder1;
    CML_Node * finder2;

    if (CML_NodeFindString(haystack1, needle, &finder1)) return YGG_FALSE;
    if (CML_NodeFindString(haystack2, needle, &finder2)) return YGG_FALSE;

    if (!finder1->data.string) return YGG_FALSE;
    if (!finder2->data.string) return YGG_FALSE;

    if (strcmp(finder1->data.string, finder2->data.string)) return YGG_FALSE;

    return YGG_TRUE;
}

★★★★★

Норм код. Не нужно править.

fsb4000 ★★★★★
()

Если C, то в конце функции помещается метка со всеми free/unlock/close, и на нее делают goto из разных мест. В gcc есть еще __attribute__ ((cleanup (cleanup_func))), вызывающий cleanup_func, когда переменная выходит из зоны видимости.

anonymous
()

Чего только не придумают чтобы не писать на плюсах.

YGGbool ret = YGGFalse;
if() goto out;
...
out:
// cleanup
return ret;

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от PPP328

Это намёк что ты совершаешь грех когда пишешь код

mittorn ★★★★★
()
Ответ на: комментарий от PPP328

Ну тогда спроси автора этих мануалов

anonymous
()
Ответ на: комментарий от itn

MISRA запрещает, NASA разрешает только under special circumstances, JPL запрещает в любом виде.

PPP328 ★★★★★
() автор топика
Ответ на: комментарий от buddhist

Самое убоюдочное изобретение человечества.

anonymous
()

Во всех мануалах громко и нецензурно кричат о том, что

В каких всех? Не читайте этот бред.

надо писать строго одну точку выхода из функции в самом ее конце, мол так надежнее

Ни в коем случае.

все unlock'и в одном месте, нельзя напортачить с забытым разлоком и проч.

С ним и так нельзя напортачить, ибо RAII всё разлочит при любом выходе из функции, в том числе по исключению.

PS. А, у вас plain C. Тогда страдайте и придумывайте себе ограничения чтобы писать на неподходящем языке хоть сколько-нибудь надёжно и безопасно.

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от slovazap

С ним и так нельзя напортачить, ибо RAII всё разлочит при любом выходе из функции, в том числе по исключению.

Бгг, хотел бы я посмотреть как RAII вызовет mutex_unlock.

PPP328 ★★★★★
() автор топика

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

Оторванные от реальности.

А скажите, господа, а как предполагается тогда писать такие функции? Через миллионы вложенных if'ов? А как это поможет читабельности и надежности?

Никак. Предполагается писать через отдельные костыли.

Собственно код, на котором мне бомбануло

Нормальный код. В нём автор всё правильно сделал.

Или что ты предлагаешь? Тут либо так, либо гоуту в конец и return retval. Хотя в таком простом примере как у тебя будет выглядеть еще хуже.

reprimand ★★★★★
()

Тю, так это даже первоклашка знает, что надо так:

static YGGBool compare_records_str(CML_Node * haystack1, CML_Node * haystack2, char * needle)
{
    CML_Node * finder1;
    CML_Node * finder2;

    return (finder1 = CML_NodeFindString(haystack1, needle, &finder1))
              && (finder2 = CML_NodeFindString(haystack2, needle, &finder2))
              && finder1->data.string
              && finder2->data.string
              && strcmp(finder1->data.string, finder2->data.string) == 0
           ? YGG_TRUE
           : YGG_FALSE;
}
На самом деле, нет.

kawaii_neko ★★★★
()
Ответ на: комментарий от PPP328

А за кучу точек выхода, особенно в коде который что-то выделяет - канделябром.

Dark_SavanT ★★★★★
()
Ответ на: комментарий от PPP328

Религиозный фанатизм не считается.

invy ★★★★★
()
Ответ на: комментарий от PPP328

То есть ты не имеешь ни малейшего представления о плюсах? Понятно.

Hint: raii - это идиома. Деструктор всегда вызывается при выходе из скоупа. Конструктор всегда вызывается при создании объекта. Если в конструктор поместить open, lock и любое выделение ресурсов, а в деструктор соотв. close, unlock и т.п., то гарантированно у тебя все будет подчищено, как только покинешь скоуп.

invy ★★★★★
()

используешь в С(plainC) само/чейто аналог defer from golang

если нужно (ну вот индус-менеджер упёрся) - то делаешь «микро"функции с единичными концевыми retами и плодишь граф вызовов - зато метрика у функции 1 точка выхода - по всему коду будет приятно-зелёная.

то что остальные(те которые твоим мануалом дня) метрики рванут в космос от такого ригоризма - ну и чё - микроменеджмент всегда такие плоды приносит -

anonymous
()
Ответ на: комментарий от gag

Ты хочешь сказать что

class A {
public:
 A() { acquireRrsource(); }
 ~A() { freeResource(); }
};

f()
{
 A res;
}

Медленнее или менее оптимизирована чем:

f()
{
  acqureResource();
...
  freeResource();
}
?

invy ★★★★★
()

с++ с RAII. на С++ можно кстати писать «как на си». но некоторым кто-то срёт в штаны и от этого у них полыхает срака от слова «С++»

ckotinko ☆☆☆
()
Ответ на: комментарий от invy

В том-то и дело, что в этом нельзя быть уверенным, т.к. Си-компиляторы разрабатывались сравнительно долго, а Си++ да, появились, но может быть просто по причине, что надо, а внутри там лишь бы компилировало более-менее: всё равно, тем, кому нужна производительность, будут использовать Си и в особых случаях по-прежнему ассемблер. Мы же не о компиляции для десктопа речь ведём.

gag ★★★★★
()
Ответ на: комментарий от gag

Чувак, для тебя есть классная новость: скоро поддержку C++ добавят в gcc, сможешь скачать и потестировать.

anonymous
()
Ответ на: комментарий от gag

Ниче, что C++ уже 34 года? Не?

Есть подозрение что ты не знаешь как C++ вообще работает...

В общем чтобы не быть голословным: Результат компиляции этого кода (С):

#include <stdio.h>
#include <stdlib.h>
int main() {
  char* p = malloc(10);
  printf("%x", p);
  free(p);
}
https://pastebin.com/ZKqzv3eQ

Результат компиляции этого кода (C++)

#include <cstdio>
#include <cstdlib>
class A {
public:
  A() { m_p = static_cast<char*>(malloc(10)); }
  ~A() { free(m_p); }
  char* m_p;
};
int main() {
  A res;
  printf("%x", res.m_p);
}
https://pastebin.com/8simNB7p

А теперь скажи мне, зачем городить макросы-гоуту и прочее говно на С, когда уже всё есть в С++?

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 3)
Ответ на: комментарий от invy

A() { m_p = static_cast<char*>(malloc(10)); }
~A() { free(m_p); }

Извращенецъ! Почему не new/delete?

Да и вообще, во втором варианте букав в 2 раза больше!

alexku
()
Ответ на: комментарий от alexku

Почему не new/delete?

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

Да и вообще, во втором варианте букав в 2 раза больше!

Буков в 2 раза больше до тех пор, пока это тривиальный пример. Потом Сишные фанатики начинают велосипедить своё ООП, как в гноме, начиная симулировать таблицу виртуальных функций и наследование на макросах - там получается лютый нечитаемый и неотлаживаемый ад, т.к. отладчик давится от того говна которое понаписано (потому что у них там структуры с указателями на функции (привет vtable) и когда происходит крэш отладчик не может нормальный стэктрэйс построить).

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от invy

Ниче, что C++ уже 34 года? Не?

Да сколько б ни было. Дело в том, что даже Си-компилятор для некоторых процессоров сравнительно новый. Не говоря уже о Си++.

Даже на Си нужно быть очень внимательным, чтобы не потерять производительность на ровном месте: C55_cgt_tips.pdf. А если начать использовать Си++, не представляю как уследить за каждой мелочью. Ведь часто надо сравнивать исходник с ассемблерным листингом. Без плюсовых абстракций это ещё реально.

А ещё формальные верификаторы для Си реальны, а вот для Си++ не слышал.

gag ★★★★★
()
Ответ на: комментарий от invy

Буков в 2 раза больше до тех пор, пока это тривиальный пример.

А когда пример становится нетривиальным, то отладочная информация начинает занимать не в 2 раза, а ещё больше. А толку? Ею даже воспользоваться без трюков никак: в стандартной поставке STL pretty printers не включены (когда проверял последний раз), не говоря уже о Qt.

gag ★★★★★
()
Ответ на: комментарий от invy

Если в конструктор поместить open, lock и любое выделение ресурсов, а в деструктор соотв. close, unlock и т.п., то гарантированно у тебя все будет подчищено, как только покинешь скоуп.

Что делать, если open или lock ошибку или таймаут вернули? Исключение из конструктора кидать?

anonymous
()

Переписать это говно вдумчиво, с эмуляцией goto:

static YGGBool compare_records_str(CML_Node * haystack1, CML_Node * haystack2, char * needle)
{
    CML_Node * finder1 = nullptr;
    CML_Node * finder2 = nullptr;
    
    YGGBool res = YGG_FALSE;

    for(;;) {
       res = CML_NodeFindString(haystack1, needle, &finder1);
       if (FAILED(res))
         break;

       res = NEVEDOMAYA_HREN(/*...*/);
       if (FAILED(res))
         break;
       
       // итд
 
       break;
    }

    if(finder1)
      FREE(finder1);

    if(finder2)
      FREE(finder1);

     // тут и далее освобождаем временные ресурсы

    return res;
}

Конструкцию вроде

if (FAILED(res))
  break;

Можно завернуть в макро, можно добавить в неё строчечки с человеко-читаемым текстом ошибки, короче много всего можно, если думать головой.

nikitos ★★★
()
Ответ на: комментарий от anonymous

Что делать, если open или lock ошибку или таймаут вернули? Исключение из конструктора кидать?

Ты это серьезно? Школьый вопрос какой-то.

A res;
if(!res.isValid())
  return false;

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от invy

Это плохой код. Начинали же с:

{
...
{
  ROLocker locker(mymutex);
  do_not_safe1();
  do_not_safe2();
  do_not_safe3();
}
...
}

А получили:

{
  TempFile t1();
  if(t1.isValid()) {
   return false;
  }
  work_with(t1);
  TempFile t2();
  if(t2.isValid()) {
   return false;
  }
  work_with(t1, t2);
}

Эта лапша с копипастами ещё хуже чем классический C подход с goto err; где будут проверки на NULL невалидных ресурсов.

Проблема RAII, то что он должен сконструировать валидный объект, который опирается на внешнюю среду.

Скоупы как-то получше выглядят для этой задачи. https://en.wikibooks.org/wiki/More_C++_Idioms/Scope_Guard CppCon 2015: Andrei Alexandrescu “Declarative Control Flow"

anonymous
()
Ответ на: комментарий от anonymous

На си тоже надо проверять результат который вернул open, и это никак не связано с raii.

invy ★★★★★
()
Ответ на: комментарий от nikitos
    if(finder1)
      FREE(finder1);

    if(finder2)
      FREE(finder1);


Кстати отличная иллюстрация, почему люди любят RAII, и не любят всякие goto cleanup;

anonymous
()
Ответ на: комментарий от PPP328

впихните с++ коммит в linux-kernel

Ты разберись, где ты работаешь - в автопроме, в JPL, в NASA или <кто там пишет ядро>.

tailgunner ★★★★★
()
Ответ на: комментарий от gag

Даже на Си нужно быть очень внимательным, чтобы не потерять производительность на ровном месте: C55_cgt_tips.pdf

C55x is a 16-bit DSP

Крутой пример.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

У Вас нога прострелена. https://isocpp.org/wiki/faq/exceptions#ctors-can-throw

Лучше ничего не бросать в C++ из конструктора и деструктора чуть-чуть невнимательности и течёт память, ресурсы или код конструктора становится похожим на чисто сишный код. А если не видно разницы, зачем городить плюсы?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.