Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Holy Wars > goto vs break & continue


Автор: Славян 13.08.12, 17:22
Немного в сторону, но подскажите, почему бы не внедрить этакий "break break" ? Он бы означал прерывание двух вложенных циклов! Можно и более.
P.S. Ну и тут же приходит в голову "continue continue" и "break continue".

Эта тема была разделена из темы "Новый стандарт C++: C++1x"

Автор: Pacific 13.08.12, 21:27
Цитата Славян @
почему бы не внедрить этакий "break break" ? Он бы означал прерывание двух вложенных циклов! Можно и более

В таких случаях следует задуматься и оформить этот кусок кода отдельной функцией. А там уже return вставить где надо, и всего делов.

Автор: trainer 14.08.12, 04:03
Цитата Славян @
Немного в сторону, но подскажите, почему бы не внедрить этакий "break break" ?
goto в руки и вперед.

Добавлено
Цитата Славян @
Ну и тут же приходит в голову "continue continue" и "break continue".
А также break break break. Морзянка break continue break. :D

Автор: B.V. 14.08.12, 07:04
Цитата trainer @
goto в руки и вперед

Типун тебе на язык

Автор: D_KEY 14.08.12, 07:35
Цитата B.V. @
Цитата trainer @
goto в руки и вперед

Типун тебе на язык

Что плохого в goto для выхода из вложенных циклов?

Автор: trainer 14.08.12, 07:41
Цитата B.V. @
Типун тебе на язык
break и continue - это и есть плохо замаскированные немного автоматизированные goto. Особенно доставляют labeled break и labeled continue в Java - вроде как и нет в нём goto. :D

Автор: D_KEY 14.08.12, 07:42
А так, могли бы ввести break/continue с меткой, на которую нужно перейти.

Автор: B.V. 14.08.12, 08:36
Цитата D_KEY @
Что плохого в goto для выхода из вложенных циклов?

В одном -- ничего. А несколько создают большую проблему понимания кода

Автор: D_KEY 14.08.12, 08:38
Цитата B.V. @
создают большую проблему понимания кода

Это может касаться любой языковой инструкции :)

Автор: B.V. 14.08.12, 08:50
Цитата D_KEY @
Это может касаться любой языковой инструкции

Приведи ка мне пример с new или break

Автор: D_KEY 14.08.12, 08:55
Привести пример чего? Лапшекода? В реализацию буста давно заглядывал :D ?

Добавлено
B.V., не надо все скатывать в холивар. goto можно использовать нормально. Как и другие конструкции можно использовать криво.

Автор: B.V. 14.08.12, 09:11
Цитата D_KEY @
Привести пример чего? Лапшекода? В реализацию буста давно заглядывал ?

Да, пример нечитабельного кода из-за вышеупомянутых операторов.
Цитата D_KEY @
B.V., не надо все скатывать в холивар. goto можно использовать нормально. Как и другие конструкции можно использовать криво.

Холивар, D_KEY, это твоя прерогатива. Другими инструкциями код запутать либо сложно, либо невозможно вовсе.

Автор: D_KEY 14.08.12, 09:25
Цитата B.V. @
Да, пример нечитабельного кода из-за вышеупомянутых операторов.

Код нечитабельным делают не сами операторы, а контекст их использования. Например, placement new в сочетании со сложным выражением получения буфера и/или типа создаваемого объекта(в который, до кучи могут передаваться еще объекты созданные по new) и пр., вполне себе делают код нечитабельным. Тоже касается и break в сочетании с попытками выхода из вложенных циклов или обработкой ошибок(в случае С).

Добавлено
Цитата B.V. @
Другими инструкциями код запутать либо сложно, либо невозможно вовсе.

:facepalm:

Автор: B.V. 14.08.12, 09:34
Цитата D_KEY @
Код нечитабельным делают не сами операторы, а контекст их использования. Например, placement new в сочетании со сложным выражением получения буфера и/или типа создаваемого объекта(в который, до кучи могут передаваться еще объекты созданные по new) и пр., вполне себе делают код нечитабельным. Тоже касается и break в сочетании с попытками выхода из вложенных циклов или обработкой ошибок(в случае С).

Ты сейчас сказал по сути следующее: операторы не при чем, код сам по себе может быть запутанным
Да неужели?

Автор: D_KEY 14.08.12, 09:35
Цитата B.V. @
Ты сейчас сказал по сути следующее: операторы не при чем, код сам по себе может быть запутанным
Да неужели?

Ага. Только не сам по себе, а кривыми руками программиста.

Это сообщение было перенесено сюда или объединено из темы "Новый стандарт C++: C++1x"

Автор: B.V. 14.08.12, 09:42
:facepalm:

Сообщения были разделены в тему "goto vs break & continue"

Это сообщение было перенесено сюда или объединено из темы "Новый стандарт C++: C++1x"

Автор: applegame 15.08.12, 00:16
Иногда goto вполне себе хорош. Была ситуация, когда в относительно длинной функции в нескольких местах надо было прервать выполнение, сделать несколько операций и выйти из функции. Наиболее простым и понятным вариантом оказался вариант с goto:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int foo() {
      ...
      goto end;
      ...
      goto end;
      ...
      goto end;
      ...
      end:
      ...
      return;
    }
Попытки написать тоже самое структурно приводят к кучам бесполезных и плохо читаемых блоков, проверок и т.п.
Так что goto иногда можно и должно применять. Религиозные фанатики идут лесом.

Автор: OpenGL 15.08.12, 03:36
Не в c++ то же самое можно сделать через try/finally.

Автор: D_KEY 15.08.12, 03:43
Цитата OpenGL @
Не в c++ то же самое можно сделать через try/finally.

В С++ нет finally(и правильно). Так что RAII - наше все :)

Автор: OpenGL 15.08.12, 04:31
Ну да, я читал в D&E о причинах его отсутствия. Но у меня не всегда получается сделать подобную вещь без goto/finally :)

Автор: Pacific 15.08.12, 05:03
Цитата applegame @
int foo() {
...
goto end;
...
goto end;
...
goto end;
...
end:
...
return;
}

В нормальном коде для таких целей используются деструкторы классов. Или у тебя хендлы и указатели все в сыром виде?

Добавлено
Цитата applegame @
в нескольких местах надо было прервать выполнение, сделать несколько операций и выйти из функции.

Или, как вариант конкретно в этом случае, оформить эти несколько операций отдельной функцией.

Автор: D_KEY 15.08.12, 05:09
Цитата OpenGL @
Ну да, я читал в D&E о причинах его отсутствия. Но у меня не всегда получается сделать подобную вещь без goto/finally :)

А примерчик можешь привести?

Добавлено
Цитата Pacific @
В нормальном коде для таких целей используются деструкторы классов. Или у тебя хендлы и указатели все в сыром виде?

А если pure C?
Различные компиляторы предлагают свои средства(вроде cleanup-атрибута в gcc), но переносимого средства нет.
Хотя есть разные либы для С, реализующие в том числе и С++-style SEH.

Автор: korvin 15.08.12, 05:18
Цитата OpenGL @
Не в c++ то же самое можно сделать через try/finally.

AFAIK, это заметно дороже, чем Go to

Автор: OpenGL 15.08.12, 05:24
Цитата D_KEY @
А примерчик можешь привести?
Сейчас сходу нет. Давно это было, а в текущем проекте все нормально в этом плане. Вспомню/найду - выложу.

Добавлено
Цитата korvin @
AFAIK, это заметно дороже, чем Go to

Ну да. Просто меня в свое время удивило, что при выпрыгивании return-ом в этом случае finally тоже выполнится.

Автор: Adil 15.08.12, 05:29
Цитата applegame @
Иногда goto вполне себе хорош. Была ситуация, когда в относительно длинной функции в нескольких местах надо было прервать выполнение, сделать несколько операций и выйти из функции. Наиболее простым и понятным вариантом оказался вариант с goto:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int foo() {
      ...
      goto end;
      ...
      goto end;
      ...
      goto end;
      ...
      end:
      ...
      return;
    }
Попытки написать тоже самое структурно приводят к кучам бесполезных и плохо читаемых блоков, проверок и т.п.
Так что goto иногда можно и должно применять. Религиозные фанатики идут лесом.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int foo()
    {
      ...
      //goto end;
      return end();
      ...
      //goto end;
      return end();
      ...
      //goto end;
      return end();
      ...
      //end:
      return end();
    }
     
    int end()
    {
      ...
      return ...;
    }

Автор: OpenGL 15.08.12, 05:32
Отдельной функции могут требоваться локальные переменные foo, и их придется передавать в параметрах. По-моему, это неудобно :)

Автор: Red 15.08.12, 05:34
Цитата
Код нечитабельным делают не сами операторы, а контекст их использования.

Любой оператор есть goto. Следовательно, заменив все конструкции структурного программирования на лежащий в их основе goto мы должны снова получить читабельный код. Но пожалуй увы :) Так что я бы сказал более высокоуровневые операторы делают код более читабельным, если применяются в подходящем контексте.

Цитата B.V. @
Ага. Только не сам по себе, а кривыми руками программиста.

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

Автор: Adil 15.08.12, 05:39
Цитата OpenGL @
Отдельной функции могут требоваться локальные переменные foo, и их придется передавать в параметрах. По-моему, это неудобно
Это следует понимать: "функции с аргументами принципиально не использую - неудобно"?

Автор: Red 15.08.12, 05:40
Цитата OpenGL @
Отдельной функции могут требоваться локальные переменные foo, и их придется передавать в параметрах. По-моему, это неудобно :)

Можно захватить.

Автор: OpenGL 15.08.12, 05:46
Цитата Adil @
Это следует понимать: "функции с аргументами принципиально не использую - неудобно"?
Нет, это следует понимать как то, что если потребуется изменить end и при этом ей станет необходимо больше/меньше параметров, то придется изменять ее объявление и вызов. Понятно, что средства рефакторинга это сделают одновременно, но зачем, если проще и наглядней через goto/деструкторы? Кроме того, если функция не возвращает значение, то этот способ не прокатит.

Автор: D_KEY 15.08.12, 05:46
Цитата korvin @
Цитата OpenGL @
Не в c++ то же самое можно сделать через try/finally.

AFAIK, это заметно дороже, чем Go to

В скалке, к примеру, даже break сделан через исключения(в библиотеке) :)

Автор: OpenGL 15.08.12, 05:47
Цитата Red @
Можно захватить.
Это если end через лямбду делать. Или еще как-то можно?

Автор: korvin 15.08.12, 06:27
Цитата D_KEY @
В скалке, к примеру, даже break сделан через исключения(в библиотеке) :)

Ну я конкретно про C++ говорил. А в скале есть Goto ("честный", а не через исключения реализованный =))?

Автор: D_KEY 15.08.12, 06:30
Цитата korvin @
Цитата D_KEY @
В скалке, к примеру, даже break сделан через исключения(в библиотеке) :)

Ну я конкретно про C++ говорил. А в скале есть Goto ("честный", а не через исключения реализованный =))?

Нет. И это логично, раз у них нет даже break.
Зато у них есть чертов finally, который смотрится еще более убого, чем в других языках, поскольку try ... catch ... finally является выражением :wall:

Автор: MyNameIsIgor 15.08.12, 06:33
Цитата D_KEY @
Зато у них есть чертов finally, который смотрится еще более убого, чем в других языках, поскольку try ... catch ... finally является выражением

Они ещё не запилили свой with? :huh:

Автор: D_KEY 15.08.12, 06:41
Цитата MyNameIsIgor @
Они ещё не запилили свой with? :huh:

Ну он, в принципе, легко делается руками.

Автор: applegame 15.08.12, 08:41
Вы тут понапредлагали и RAII и отдельной функцией и лямбдой. Вопрос: зачем? Самый понятный и читабельный вариант с goto. Какой смысл избавляться от goto исключительно ради избавления от goto? Лично мне религия позволяет изредка использовать его.

Автор: Повстанець 15.08.12, 08:54
Цитата applegame @
Вы тут понапредлагали и RAII и отдельной функцией и лямбдой. Вопрос: зачем? Самый понятный и читабельный вариант с goto. Какой смысл избавляться от goto исключительно ради избавления от goto? Лично мне религия позволяет изредка использовать его.
Никогда с goto не получается ни понятно, ни читабельно. Получается абсолютно write-only код для понимания которого нужно прогонять его в отладчике снова и снова с целой кучей кейсов. Да ещё и на бумажке чертить, что в каком случае происходит. За такое нужно гнать с работы!

Автор: MyNameIsIgor 15.08.12, 08:55
Цитата applegame @
Вы тут понапредлагали и RAII и отдельной функцией и лямбдой. Вопрос: зачем? Самый понятный и читабельный вариант с goto. Какой смысл избавляться от goto исключительно ради избавления от goto? Лично мне религия позволяет изредка использовать его.

Почему-то с RAII получается без всяких goto, хотя специально его не избегаю, но всё равно не приходится использовать.

Автор: OpenGL 15.08.12, 09:08
Цитата Повстанець @

Никогда с goto не получается ни понятно, ни читабельно. Получается абсолютно write-only код для понимания которого нужно прогонять его в отладчике снова и снова с целой кучей кейсов. Да ещё и на бумажке чертить, что в каком случае происходит. За такое нужно гнать с работы!
Что в коде на предыдущей странице было непонятного? Все читабельно, а если еще и комментариев добавить, то даже проще чем RAII будет.

Автор: D_KEY 15.08.12, 09:13
Цитата OpenGL @
проще чем RAII будет.

Дело не только в простоте, сколько в надежности. Чем меньше человеческого фактора, тем лучше.

Автор: Повстанець 15.08.12, 09:17
Цитата OpenGL @
Что в коде на предыдущей странице было непонятного?
Это в этом чтоли?
Цитата applegame @
int foo() {
...
goto end;
...
goto end;
...
goto end;
...
end:
...
return;
}
Ну давай посмотрим. В том виде, как он есть сейчас:
Цитата applegame @
int foo() {
...
goto end;
...\\никогда не выполнится
goto end;\\никогда не выполнится
...\\никогда не выполнится
goto end;\\никогда не выполнится
...\\никогда не выполнится
end:
...
return;
}
Я конечно подозреваюю, что эти goto содержатся внутри циклов и ветвей условий. Вот собственно так и получается write-only код, который при командной разработке не стоит ровным счётом ни-хе-ра.

Автор: OpenGL 15.08.12, 09:23
Цитата D_KEY @
Дело не только в простоте, сколько в надежности. Чем меньше человеческого фактора, тем лучше.
Ну это да. Но опять-же, мое имхо - к RAII не всегда можно привести. И да, предчувствуя вопрос :)
Цитата OpenGL @
Цитата D_KEY @
А примерчик можешь привести?
Сейчас сходу нет. Давно это было, а в текущем проекте все нормально в этом плане. Вспомню/найду - выложу.

Автор: D_KEY 15.08.12, 09:26
:D

Автор: KILLER 15.08.12, 09:54
Цитата Повстанець @
Я конечно подозреваюю, что эти goto содержатся внутри циклов и ветвей условий. Вот собственно так и получается write-only код, который при командной разработке не стоит ровным счётом ни-хе-ра.

С чего ты взял? Потому что так написано в умной книжке? goto нормально можно применять, в некоторых случаях, его не нужно применять всегда и всюду. Его нужно применять там где он будет предпочтительнее. Ничего плохого в нем не вижу.

Автор: OpenGL 15.08.12, 09:57
Цитата Повстанець @
Я конечно подозреваюю, что эти goto содержатся внутри циклов и ветвей условий. Вот собственно так и получается write-only код, который при командной разработке не стоит ровным счётом ни-хе-ра.
Ну раз догадываешься, как оно должно быть, зачем умничать и говорить, что там что-то не выполнится? :) Опять-же, то, что он write-only - спорно. Не зная, как устроены деструкторы, в аналогичном RAII коде ты будешь разбираться гораздо дольше.

Автор: Повстанець 15.08.12, 10:01
Цитата OpenGL @
Не зная, как устроены деструкторы, в аналогичном RAII коде ты будешь разбираться гораздо дольше.
Не зная как устроены деструкторы ты не будешь разбираться в аналогичном коде. Во всяком случае не у нас на проекте. Инфа 146%.

Автор: MyNameIsIgor 15.08.12, 10:16
Цитата Повстанець @
Цитата OpenGL @
Не зная, как устроены деструкторы, в аналогичном RAII коде ты будешь разбираться гораздо дольше.
Не зная как устроены деструкторы ты не будешь разбираться в аналогичном коде. Во всяком случае не у нас на проекте. Инфа 146%.

Супер сказано :good:

Автор: OpenGL 15.08.12, 10:18
Цитата Повстанець @
Не зная как устроены деструкторы ты не будешь разбираться в аналогичном коде. Во всяком случае не у нас на проекте. Инфа 146%.

Это конечно хорошо, но не отменяет того, что иногда с goto код действительно становится понятнее и проще :)

Автор: D_KEY 15.08.12, 10:30
Цитата OpenGL @
Это конечно хорошо, но не отменяет того, что иногда с goto код действительно становится понятнее и проще :)

Какие ваши доказательства?

Автор: OpenGL 15.08.12, 10:36
Цитата D_KEY @
Какие ваши доказательства?
Да хоть предмет спора (выход из функции) или выход из вложенных циклов. Сам же знаешь, что в меру иногда вполне неплохо.

Автор: D_KEY 15.08.12, 10:49
Выход из вложенных циклов - да.
Выход из функции - только для pure C. В С++ это костыль. Ну разве что при активном использовании сишного API, но и тут можно обертки завести(а в некоторых случаях достаточно shared_ptr + кастомного deleter'а).

Автор: Adil 15.08.12, 10:50
Вложенные циклы уже сами по себе настораживают.

Автор: Qraizer 15.08.12, 11:50
Цитата korvin @
AFAIK, это заметно дороже, чем Go to
Отнюдь. Локальная размотка стека дёшева, это размотка, затрагивающая исполнительные блоки разных функций, может быть дорога. В тому же универсальные решения учитывают любые способы покинуть исполнительный блок, включая longjmp(). Если пробовать писать свой подобный велосипед, универсальный не получится проще и/или быстрее.

Автор: Chow 15.08.12, 12:26
Цитата applegame @
Иногда goto вполне себе хорош. Была ситуация, когда в относительно длинной функции в нескольких местах надо было прервать выполнение, сделать несколько операций и выйти из функции. Наиболее простым и понятным вариантом оказался вариант с goto

Может все же проще:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    do {
     ...
     break;
     ...
     break;
     ...
     break;
     ...
    } while (false);

?

Автор: michspar 15.08.12, 18:35
или так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void process(data)
    {
      ...
      return;
      ...
      return;
      ...
      return;
      ...
      return;
    }
     
    void main()
    {
    //...initializing
      process(data);
    //...releasing
    }

Автор: applegame 15.08.12, 19:59
Цитата D_KEY @
Ну разве что при активном использовании сишного API, но и тут можно обертки завести(а в некоторых случаях достаточно shared_ptr + кастомного deleter'а).
Именно, так и было, много сишного апи. Обертки только усложняют и замедляют. Еще раз повторяю, зачем городить обертки, RAII и прочее только ради того чтобы сказать самому себе: "я победил goto!!!". goto применяется крайне редко, как правило в очень простых и понятных случаях вроде описанных.

Добавлено
Цитата Chow @
Может все же проще
Вряд ли, терпеть ненавижу циклы, которые на самом деле не циклы. Видим do и думаем, что сейчас что-то будет крутиться, а на самом деле все ради break. Сомнительное решение.

Автор: MyNameIsIgor 15.08.12, 20:28
Цитата applegame @
Именно, так и было, много сишного апи. Обертки только усложняют и замедляют. Еще раз повторяю, зачем городить обертки, RAII и прочее только ради того чтобы сказать самому себе: "я написан понятный, надёжный, простой код!!!"

fixed

Автор: applegame 15.08.12, 20:42
Цитата MyNameIsIgor @
fixed
cut and distorted.

Автор: D_KEY 16.08.12, 06:39
Цитата applegame @
Еще раз повторяю, зачем городить обертки, RAII и прочее только ради того чтобы сказать самому себе: "я победил goto!!!".

Так не для этого городить надо. Идиома RAII тем хороша, что ты с одной стороны, знаешь, когда и что произойдет, а с другой - не делаешь это руками, т.к. все происходит в автоматическом режиме. На этом фоне даже finally выглядит костылем, а ты про goto...

Автор: applegame 16.08.12, 10:38
Цитата D_KEY @
Так не для этого городить надо. Идиома RAII тем хороша, что ты с одной стороны, знаешь, когда и что произойдет, а с другой - не делаешь это руками, т.к. все происходит в автоматическом режиме. На этом фоне даже finally выглядит костылем, а ты про goto...
Не-не-не. Я не спорю. RAII - это отличная штука. Я ей постоянно пользуюсь. Я к тому, что глупо категорично заявлять, что goto никогда не надо использовать. Иногда, очень редко, goto оказывается проще, быстрее и понятнее, чем что-либо другое.

Автор: michspar 17.08.12, 08:32
Цитата applegame @
Иногда, очень редко, goto оказывается проще, быстрее и понятнее, чем что-либо другое

Конкретный пример в студию.

Автор: applegame 17.08.12, 08:38
Цитата michspar @
Конкретный пример в студию.
Был выше. Но похоже некоторым нравится дописывать еще десяток строк, лишь бы не нарушать табу.
Почитайте: http://habrahabr.ru/post/114211/

Автор: Повстанець 17.08.12, 10:25
Цитата applegame @
Да-да. Всё околоайтишное говницо со всего СНГ оседает именно на хабре.

На самом деле даже в том рафинированном примере, будь A, B, C, D, E более одной строчки, или как там написано:
Цитата
это некоторые операции, а не вызов функции! Вполне возможно, что они используют массу локальных переменных. И вполне возможно, что они меняют их состояние.
Даже тот вырвиглазный ужос во втором примере будет более понятен, нежели неизвестно куда ведущие goto. Закручивающие в циклы и ветвления (ветвлениz прямо во внутрь цикла sic!), по неочевидным (а после того, как код перестанет быть тривиальным -- так и вообще не понятным) условиям, которые через месяц не сможет разобрать даже их автор.

Автор: KILLER 17.08.12, 10:35
Цитата Повстанець @
Даже тот вырвиглазный ужос во втором примере будет более понятен, нежели неизвестно куда ведущие goto. Закручивающие в циклы и ветвления (ветвлениz прямо во внутрь цикла sic!), по неочевидным (а после того, как код перестанет быть тривиальным -- так и вообще не понятным) условиям, которые через месяц не сможет разобрать даже их автор.

Зачем рыть себе могилу? Ты просто накладываешь на использование goto шаблон, по которому этот код у тебя обязательно разрастется до более чем 1 строчки, и станет до жути не понятным и запутанным. Так ты просто не юзай в этих местах goto. Юзай его там, где он не будет усложнять логику и запутывать программиста.
А то как послушаешь так на словах все такие рассякие, у всех функции не больше 80 строк, ни у кого нет вложенных циклов, ни у кого нет костыльных dynamic/static/reinterped/const кастов. У всех все просто кульно и круто. А на деле, практически любой код содержит в себе запутанные места, вложенные циклы, либо переизбыток функций в классах, всякие магические касты, непонятные и запутанные алгоритмы и т.д.

Автор: Повстанець 17.08.12, 10:39
Цитата KILLER @
Зачем рыть себе могилу? Ты просто накладываешь на использование goto шаблон, по которому этот код у тебя обязательно разрастется до более чем 1 строчки, и станет до жути не понятным и запутанным. Так ты просто не юзай в этих местох goto. Юзай его там, где он не будет усложнять логику и запутывать программиста.
Это не мой пример, а того чудака с хабра. Я даже знаю как он его придумывал:

Сидит, значит, надо мол что то изообразить. Нарисовал схему. Не, не получается не обойтись без goto. Добавил ещё блоков, ч0рт -- опять не получается. Добавил стрелочик... та ну ёшкин кот... снова не то. И так пока совсем что то вырвиглазного не вышло.

Автор: KILLER 17.08.12, 10:40
А то вон, помню один в тематике написал типа:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    some_value SomeFunc(bool bFlag)
    {
       switch(bFlag)
       {
          case true:
             ...
          break;
          case false:
             ...
          break;
       }
    }

Ну вообще как красиво и понятно все... Так и напрашивается default сюда :D

Добавлено
Цитата Повстанець @
Это не мой пример, а того чудака с хабра. Я даже знаю как он его придумывал:

Сидит, значит, надо мол что то изообразить. Нарисовал схему. Не, не получается не обойтись без goto. Добавил ещё блоков, ч0рт -- опять не получается. Добавил стрелочик... та ну ёшкин кот... снова не то. И так пока совсем что то вырвиглазного не вышло.

Не, я к тому, что не стоит так категорично относится к goto, в некоторых случаях он всетаки полезен и не запутывает, а юзать его с целью запутать программиста - ну так это уже ССЗБ.

Автор: michspar 17.08.12, 12:16
Цитата applegame @
Был выше.

Вот этот-то?
Цитата applegame @
Иногда goto вполне себе хорош. Была ситуация, когда в относительно длинной функции в нескольких местах надо было прервать выполнение, сделать несколько операций и выйти из функции. Наиболее простым и понятным вариантом оказался вариант с goto:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int foo() {
      ...
      goto end;
      ...
      goto end;
      ...
      goto end;
      ...
      end:
      ...
      return;
    }
Попытки написать тоже самое структурно приводят к кучам бесполезных и плохо читаемых блоков, проверок и т.п.
Так что goto иногда можно и должно применять. Религиозные фанатики идут лесом.

Так я тоже писал, как его правильно реализовывать надо. И это не костыль для обохода goto. Это разделение частей кода на логические части.

Автор: michspar 17.08.12, 12:32
Цитата applegame @

Читал. Автор предлагает (синтетическую, кстати) схему:
user posted image
реализовать с помощью goto вот так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    if (a)
    {
        A;
        goto L3;
    }
    L1:
    if (b)
    {
    L2:
        B;
    L3:
        C;
        goto L1;
    }
    else if (!c)
    {
        D;
        goto L2;
    }
    E;


Но, во избежание goto, приводит какой-то монструозный вариант (который я тут даже показывать не хочу), вместо того, чтобы приложить мозг и родить хотя бы что-нибудь такое:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    bool a, b, c;
     
    if ( a )
    {
        A();
        C();
    }
     
    do
    {
        while ( b )
        {
            B();
            C();
        }
     
        if ( c )
            break;
     
        D();
        B();
        C();
    } while ( true );
     
    E();

И тут (внезапно) выясняется, что в этом алгоритме два цикла, один вложеный в другой. А если ещё и добавить контекста, то полюбому получится, что задачу можно решить, разложив алгоритм на подалгоритмы, чтобы такая безумная схема не понадобилась в принципе. Goto действительно легче. Для того, кто пишет, потому что позволяет сериализовывать мысли в код с минимумом усилий. Как правильно тут уже упоминали, такой код - это write only код и в продакшене нам такой код не нужен :no:

Автор: applegame 17.08.12, 12:58
michspar, ваш пример скопипастен из того же холивара на хабре, там же читайте возражения. :)

Добавлено
Цитата Повстанець @
Да-да. Всё околоайтишное говницо со всего СНГ оседает именно на хабре.
Отличная аргументация.

Автор: Повстанець 17.08.12, 13:01
Цитата applegame @
Просто ахренительная аргументация.
Что могу сказать? Какие аргументы, такие и контраргументы. :-?

Автор: applegame 17.08.12, 13:03
Цитата Повстанець @
Что могу сказать? Какие аргументы, такие и контраргументы. :-?
Откровенная ложь с честными глазами.

Автор: michspar 17.08.12, 13:06
Цитата applegame @
ваш пример скопипастен из того же холивара на хабре

Глубоко не вчитывался. Честно выдумал находу. Нечего возразить? Так и запишем.

Автор: applegame 17.08.12, 13:07
Цитата michspar @
Так я тоже писал, как его правильно реализовывать надо. И это не костыль для обохода goto. Это разделение частей кода на логические части.
Иногда такое разделение только ухудшает читабельность кода. Собственно тут повторяются аргументы из комментов статьи на хабре.

Добавлено
Цитата michspar @
Нечего возразить? Так и запишем.
Вы товарищ, похоже сам с собой разговариваете. :D

Добавлено
А по существу, ваши A(), B() и.т.п. могут требовать доступ к неким локальным переменным. И что теперь каждый раз их передавать внутрь этих функций? Отличное решение и, главное, такое прозрачное.

Автор: michspar 17.08.12, 13:13
Вот неотвеченый пример из конца того же холивара:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            public int[,] ReadByCoil( out int npts )
            {
                int[,] res = null;
                npts = 0;
                if ( m_curArea != 0 ) goto _1;
            _2:
                if ( !StartNewSect() ) return null;
            _1:
                res = ReadCoil();
                if ( res == null ) goto _2;
                npts = m_npt;
                return res;
            }

Очевидно же:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            public int[,] ReadByCoil( out int npts )
            {
                int[,] res = null;
     
                npts = 0;
     
                if ( m_curArea == 0 && !StartNewSect() ) return null;
     
                res = ReadCoil();
     
                if ( res == null ) goto _2;
     
                npts = m_npt;
     
                return res;
            }


Добавлено
Цитата applegame @
ваши A(), B() и.т.п. могут требовать доступ к неким локальным переменным...

Цитата michspar @
если ещё и добавить контекста, то полюбому получится, что задачу можно решить, разложив алгоритм на подалгоритмы, чтобы такая безумная схема не понадобилась в принципе

Автор: Повстанець 17.08.12, 13:53
Цитата applegame @
Откровенная ложь с честными глазами.
Ну давай посмотрим. Надеюсь ты статью читал? Если не читал, рассказываю в двух словах: сначала автор брешет про комбинационные схемы. Потом выдал пару слов про RS триггеры, где ляпнул абсолютно не имеющую смысла хрень. Потом начался какой то детский лепет про ассемблер, где автор опять же успел наврать аж два раза. Потом рассмотрение каких то рафинированных примеров, и попытка анализа их с точки зрения производительности там и расхода памяти, где автор уже начинает брехать аки сивый конь. Что то там про лишние операции сравнения и ассемблерные листинги, экономию тактов, совершенно не зная даже о чём говорит. Плять кому нужно экономить десяток другой тактов, если это спровоцирует внеочередной сброс и перезагрузку кэша? Рассуждения на тему исключений С++ вообще за гранью добра и бобра. Автор слабо разбирается во всех сферах, к которым пытается аппелировать и С++ знает посредственно.

В этом, конечно, весь хабр, но зачем сюда то всякую гадость тянуть?

Автор: D_KEY 17.08.12, 13:58
Цитата Повстанець @
В этом, конечно, весь хабр

:yes-sad:

Автор: applegame 17.08.12, 14:14
Цитата Повстанець @
В этом, конечно, весь хабр, но зачем сюда то всякую гадость тянуть?
Комментов ради. Статью читал и комментарии тоже. Все авторы брешут в той или иной степени, сознательно или по ошибке, по всякому. Аргументировать надо по существу, а не "ты - ...".

Автор: MyNameIsIgor 17.08.12, 14:16
Цитата applegame @
Цитата Повстанець @
В этом, конечно, весь хабр, но зачем сюда то всякую гадость тянуть?
Комментов ради.

А мы то, наивные, надеялись, что для подкрепления вашей позиции...
Впрочем, я догадывался.

Автор: applegame 17.08.12, 14:18
Цитата MyNameIsIgor @
А мы то, наивные, надеялись, что для подкрепления вашей позиции...
Впрочем, я догадывался.
Сначало надо научится внимательно читать. Статья была приведена для примера варианта кода, в котором goto оправдан.

Автор: MyNameIsIgor 17.08.12, 14:20
Цитата applegame @
Статья была приведена для примера варианта кода, в котором goto оправдан.

Так наглядно же в стихах и песнях объяснили, что нифига он не оправдан в этих примерах...

Автор: applegame 17.08.12, 14:20
И вообще, вы хоть в курсе какая именно у меня точка зрения относительно goto? Думаю, что нет.

Добавлено
Цитата MyNameIsIgor @
Так наглядно же в стихах и песнях объяснили, что нифига он не оправдан в этих примерах...
Точно также там есть объяснения, что оправдан. Но вы как обычно видите только то, что хотите видеть.

Автор: MyNameIsIgor 17.08.12, 14:27
Цитата applegame @
у меня

Цитата applegame @
точка зрения

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

Автор: applegame 17.08.12, 14:39
Цитата MyNameIsIgor @
Взаимоисключающие параграфы. Вы характеризуетесь тем, что где-то что-то почитали, посмотрели на автора, признали его за "авторитета" и страшно удивляетесь, когда другие данного персонажа в мужской половой орган не ставят.
Вы можете только повторять чьё-то мнение, но своё, да ещё и с обоснование - не, в вашем случае это фантастика.
Фантастика - это увидеть пост от MyNameIsIgor без оскорблений. Если у вас такая низкая оценка моих способностей, то просто не дискутируйте со мной.

Автор: MyNameIsIgor 17.08.12, 14:42
Цитата applegame @
Фантастика - это увидеть пост от MyNameIsIgor без оскорблений.

Что, правда вызывает батхёрт? Ну, ничего - поболит и пройдёт :D
Цитата applegame @
Если у вас такая низкая оценка моих способностей, то просто не дискутируйте со мной.

Так я и не дискутирую, а всего лишь высказываю своё отношение к происходящему в теме.

Автор: applegame 17.08.12, 14:49
Цитата MyNameIsIgor @
Ну, ничего - поболит и пройдёт :D
Спасибо, что поделились своим бесценным опытом. :D
Цитата MyNameIsIgor @
Так я и не дискутирую, а всего лишь высказываю своё отношение к происходящему в теме. :trollface:
fixed

Автор: Qraizer 17.08.12, 15:41
Вы, видимо, забыли об этой теме. Ключевые моменты:
Цитата Qraizer @
Ха. Вы, похоже, никогда не использовали goto по-настоящему:
Слабонервным от структурности не смотреть.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <windows.h>
    #include <io.h>
    #include <conio.h>
    #include <fstream>
    #include <iostream>
     
    int main()
    {
      int i, j, k, l;
     
      if (_access("savedstate.dat", 4) == 0)     // Проверить, нет ли сохранённого состояния от предыдущего запуска
      {                                             // Если есть, восстановить и продолжиться
        std::ifstream inFile("savedstate.dat");
        bool          ok = true;
     
        ok = ok && inFile.read(reinterpret_cast<char*>(&i), sizeof(i)).gcount() == sizeof(i);
        ok = ok && inFile.read(reinterpret_cast<char*>(&j), sizeof(j)).gcount() == sizeof(j);
        ok = ok && inFile.read(reinterpret_cast<char*>(&k), sizeof(k)).gcount() == sizeof(k);
        ok = ok && inFile.read(reinterpret_cast<char*>(&l), sizeof(l)).gcount() == sizeof(l);
        if (ok && (inFile.good() || inFile.eof()))
        {
          std::cout << "The saved state is successfully restored." << std::endl;
          goto resume;
        }
        else return std::cerr << "The saved state restoring error. State file is wrong." << std::endl,
                    1;
      }
     
      for (i=0; i<10; ++i)
      {
        for (j=i; j<10; ++j)
        {
          for (k=j; k<10; ++k)
          {
            for (l=k; l<10; ++l)
            {
    resume:
              if (kbhit() && getch() == '\x1B')     // Проверить, не просят ли нас ESCпом прерваться
              {                                     // Если да, сохранить состояние и выйти
                if (_access("savedstate.dat", 2) == 0) remove("savedstate.dat");
              
                std::ofstream outFile("savedstate.dat");
                bool ok = true;
              
                ok = ok && outFile.write(reinterpret_cast<char*>(&i), sizeof(i)).good();
                ok = ok && outFile.write(reinterpret_cast<char*>(&j), sizeof(j)).good();
                ok = ok && outFile.write(reinterpret_cast<char*>(&k), sizeof(k)).good();
                ok = ok && outFile.write(reinterpret_cast<char*>(&l), sizeof(l)).good();
                if (!(ok && (outFile.good() || outFile.eof())))
                  return std::cerr << "The state saving error." << std::endl,
                        1;
                return std::cout << "The state successfully saved." << std::endl,
                       2;
              }
     
              /* ... */
     
              std::cout << "i = " << i << ", j = " << j << ", k = " << k << ", l = " << l << std::endl;
              Sleep(1);
            }
          }
        }
      }
      
      if (_access("savedstate.dat", 2) == 0) remove("savedstate.dat");
      return 0;
    }
Цитата Dark_Sup @
Убойно. Переходить по goto в середину вложенных циклов... таких номеров я даже в цирке не видел.
Цитата Qraizer @
Это была какая-то DOS программка с очччень длительным временем работы. В связи с чем сохранение/восстановление было не лишним. Чем занималась, уже не помню, но как-то для похожей дискуссии на Реалкодинге набросал по памяти основную идею.
Цитата Qraizer @
Для сравнения, кто возьмётся переписать мой пример без goto?
Цитата ЫукпШ @
Так ?
...
Цитата Qraizer @
... Параметры циклов меняются внутри их тел; в код добавилось 220 (update: уже больше, в оригинале было три цикла) сравнений; производительность упала... Нет, без потери производительности и при этом структурно - можно, но читабельность будет (ещё) ниже плинтуса.
Цитата Qraizer @
Как-то так, вроде.
Ещё много строчек.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <windows.h>
    #include <io.h>
    #include <conio.h>
    #include <fstream>
    #include <iostream>
     
    int main()
    {
      int i, j, k, l;
      int i0 = 0,
          j0 =i0,
          k0 =j0,
          l0 =k0;
     
      if (_access("savedstate.dat", 4) == 0)        // Проверить, нет ли сохранённого состояния от предыдущего запуска
      {                                             // Если есть, восстановить и продолжиться
        std::ifstream inFile("savedstate.dat");
        bool          ok = true;
     
        ok = ok && inFile.read(reinterpret_cast<char*>(&i0), sizeof(i0)).gcount() == sizeof(i0);
        ok = ok && inFile.read(reinterpret_cast<char*>(&j0), sizeof(j0)).gcount() == sizeof(j0);
        ok = ok && inFile.read(reinterpret_cast<char*>(&k0), sizeof(k0)).gcount() == sizeof(k0);
        ok = ok && inFile.read(reinterpret_cast<char*>(&l0), sizeof(l0)).gcount() == sizeof(l0);
        if (ok && (inFile.good() || inFile.eof()))
          std::cout << "The saved state is successfully restored." << std::endl;
        else return std::cerr << "The saved state restoring error. State file is wrong." << std::endl,
                    1;
      }
     
      for (i=i0; i<10;)
      {
        for (j=j0; j<10;)
        {
          for (k=k0; k<10;)
          {
            for (l=l0; l<10; ++l)
            {
              if (kbhit() && getch() == '\x1B')     // Проверить, не просят ли нас ESCпом прерваться
              {                                     // Если да, сохранить состояние и выйти
                if(_access("savedstate.dat", 2)==0) remove("savedstate.dat");
     
                std::ofstream outFile("savedstate.dat");
                bool          ok = true;
     
                ok = ok && outFile.write(reinterpret_cast<char*>(&i), sizeof(i)).good();
                ok = ok && outFile.write(reinterpret_cast<char*>(&j), sizeof(j)).good();
                ok = ok && outFile.write(reinterpret_cast<char*>(&k), sizeof(k)).good();
                ok = ok && outFile.write(reinterpret_cast<char*>(&l), sizeof(l)).good();
                if (!(ok && (outFile.good() || outFile.eof())))
                  return std::cerr << "The state saving error." << std::endl,
                         1;
                return std::cout << "The state successfully saved." << std::endl,
                       2;
              }
     
              /* ... */
     
              std::cout << "i = " << i << ", j = " << j << ", k = " << k << ", l = " << l << std::endl;
              Sleep(1);
            }
            l0 = ++k;
          }
          l0 = k0 = ++j;
        }
        l0 = k0 = j0 = ++i;
      }
     
      if (_access("savedstate.dat", 2)==0) remove("savedstate.dat");
      return 0;
    }
Не то, чтобы вообще без потерь, но плохонького оптимизатора будет достаточно. Помню, что тогда я так и не допёр до этого решения. Минут через 20, включающие три попытки отладиться, плюнул и написал в лоб, т.е. с goto. Потратил 10 секунд и ни капли отладки. Сейчас вот пошло с третьего раза и заняло 10 минут.

Автор: OpenGL 17.08.12, 15:53
О, а я все ждал, когда Qraizer выложит свой код с goto :) Уже видел его на другом форуме.
Я кстати вспомнил, где использовал goto для выхода из функции. В дельфи, где отсутствуют нормальные деструкторы :)

Автор: D_KEY 17.08.12, 15:56
Цитата OpenGL @
В дельфи, где отсутствуют нормальные деструкторы :)

Там же присутствует "нормальный" finally

Автор: OpenGL 17.08.12, 16:00
Цитата D_KEY @
Там же присутствует "нормальный" finally

Во-первых, решение с goto в данном случае мне кажется более логичным, во-вторых, тогда я не знал, что при выпрыгивании он тоже выполнится :)

Автор: Adil 17.08.12, 20:25
Не, там неправильно было.

Автор: michspar 17.08.12, 21:32
Цитата Qraizer @
Переходить по goto в середину вложенных циклов...

Изыйди, аццкий сотона. Чур меня, чур! :wall:

Автор: Славян 22.08.12, 17:27
Жесть какая. Сколько левого то написали.
Суть то простая до неприличия: По закону Мерфи 90% циклов в программе - одинарные, 9% - двойные, 0,9% - тройные и т.д.
Посему я и хотел всего лишь второй уровень забацать, чтобы работалось легко не в 90%, а в 99% случаев. Всего и идей то !!!

Автор: Kray74 22.08.12, 18:24
Возможно, уже говорили, но в PHP (или еще где, уже не помню), можно написать
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    break(2)

т.е. выход из двух циклов

Автор: applegame 22.08.12, 20:32
Цитата Славян @
По закону Мерфи 90% циклов в программе - одинарные, 9% - двойные, 0,9% - тройные и т.д
Странный какой-то у вас закон. Закон Мерфи для циклов в C++, на самом деле, звучит так:
В любом цикле с вложенностью два и больше обязательно появится необходимость выйти из самого внутреннего за пределы самого внешнего.

Автор: OpenGL 23.08.12, 05:48
Цитата applegame @
В любом цикле с вложенностью два и больше обязательно появится необходимость выйти из самого внутреннего за пределы самого внешнего.
Не в любом. В алгоритме Флойда не надо :)

Автор: applegame 23.08.12, 08:19
Цитата OpenGL @
Не в любом. В алгоритме Флойда не надо :)
По закону Мерфи - в любом. ;) http://ru.wikipedia.org/wiki/Закон_Мёрфи

Автор: Славян 23.08.12, 12:53
Я просто взял часть закона:
В любой, казалось бы работающей программе, 90% циклов - одинарные и выход из них есть, но в 10%, 1%, ...случаев - это двойные, тройные и т.д. циклы, из которых
Цитата applegame @
обязательно появится необходимость выйти из самого внутреннего за пределы самого внешнего
, и вот тут то прога и ...

Автор: amk 28.08.12, 18:43
В наиболее общем виде закон Мёрфи можно наверно сформулировать так: "Если неприятность в принципе может случиться, она обязательно произойдет".

Автор: korvin 28.08.12, 19:47
Цитата amk @
В наиболее общем виде закон Мёрфи можно наверно сформулировать так: "Если неприятность в принципе может случиться, она обязательно произойдет".

Такой же перевод и в вике, хотя мне больше нравится более дословный: "если что-то может пойти не так, то оно обязательно пойдет (не так)"

Автор: applegame 29.08.12, 10:16
Даже если вас невозможно понять неправильно, все равно найдется человек который поймет вас неправильно.

Автор: JoeUser 13.04.15, 21:01
Цитата KILLER @
:bad:


Может тебе еще не нравится оператор "goto"?! >:(

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: JoeUser 13.04.15, 21:50
KILLER, :blush:
Скрытый текст
Цитата KILLER @
:bad: :bad: :bad: :bad: :bad:

Лан ... давай посмотрим, вот задачка. Нужно реализовать сей алгоритм управления:
user posted image
a,b,c - флажки состояния, A,B,C,D,E - функции-обработчики, которые изменяют флажки в том числе
Реализация с GOTO:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int main() {
      bool a = false, b=false, c=false;
      if (a) {
        A();
        goto L3;
      }
      L1:
      if (b) {
        L2:
        B();
        L3:
        C();
        goto L1;
      } else
      if (!c) {
        D();
        goto L2;
      }
      E();
      return 0;
    }


Давай почитаем ТВОЙ КОД без GOTO?
И убедительнейшая просьба - давай просто код, про корабли, бороздящие просторы вселенной не надо, летали - знаем! :lol:


Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: KILLER 13.04.15, 22:29
Цитата JoeUser @
Лан ... давай посмотрим, вот задачка. Нужно реализовать сей алгоритм управления:

Как то так?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
     
    void A()
    {
        std::cout << "A" << std::endl;
    }
     
    void B()
    {
        std::cout << "B" << std::endl;
    }
     
    void C()
    {
        std::cout << "C" << std::endl;
    }
     
    void D()
    {
        std::cout << "D" << std::endl;
    }
     
    void E()
    {
        std::cout << "E" << std::endl;
    }
     
    int main()
    {
        bool a = false, b=false, c=false;
        if(a)
        {
            A();
            C();
        }
        else
        {
            while(b)
            {
                B();
                C();
                if (! b && !c)
                {
                    D();
                    B();
                    C();
                }
                else
                {
                    E();
                    break;
                }
            }
        }
        return 0;
    }

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

Добавлено
Да и твоя схема на картинке слишком мутная тоже, обычно есть задача - ее и решаем. А у тебя на схеме те же метки нарисованы. Что реализацию, что схема - гавно. Хрен что разберешь.

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: JoeUser 13.04.15, 23:10
Цитата KILLER @
в жопу засовывать, откуда они собственно и выросли

Несколько быдловатая манера общения, ну да ладно ...

Я тебя просил привести работающий код, а ты что состряпал?

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int main()
    {
        bool a = false, b=false, c=false;
        if(a)
            // ЕСЛИ СЮДА НЕТ ПЕРЕХОДА ИЗ КОДА ПОСЛЕ, ЗАЧЕМ ЭТОТ БЛОК В ПРИНЦИПЕ?
            // a == false на входе же, сразу после инициализации, блок никогда не будет исполнен!
        {
            A();
            C();
        }
        else ........


По твоему коду a == false и b == false - сразу выход, а "с" кто проверять будет???.
Садись, двойка!

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: Qraizer 14.04.15, 01:15
M
Вас в Холивары перенести или сами перенесётесь?


Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: JoeUser 14.04.15, 02:09
Не не не, я умываю руки.
Немножко поразмышлял, сделал код без Goto ... Горррраздо понятнее! :lool:

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: KILLER 14.04.15, 07:41
Цитата JoeUser @
Несколько быдловатая манера общения, ну да ладно ...

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

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: JoeUser 14.04.15, 10:47
Qraizer, раздели пожалуйста тему (то, что про goto -в холивары), не трогая посты по тематиен ТС

Цитата KILLER @
С чего ты взял? У тебя что схема - что код, черт руку сломит.

Согласен, что схема с потолка.
Хорошо, спецом порылся в своих Zakroma of Rodina :) и нашел блок-схему реального проекта, который я реализовывал лет 10 назад. Признаюсь, тогда тоже свято верил в "зло" от использования goto, но тогдашняя реализация получилась монстром. Сейчас об этом даже смешно вспоминать. Надо было просто послать всех в лес, виместе с их теоремами, сделать все гораздо короче и сэкономить кучу времени.

Если ты такой правильный, хочешь реального примера - прилагаю сию БС, давай накидай мне хотя-бы схематично без goto. Посмотрим на понятность тобою написанного. Если о5 начнешь про корабли, которые упорно бороздят ... станет все понятно.

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

АлгоритмAlgo.png (, : 708)

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: KILLER 14.04.15, 11:56
Цитата JoeUser @
давай накидай мне хотя-бы схематично без goto.

Ну вот хотя бы, хотя тут еще можно разбить на функции некоторе части.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class Document
    {
    public:
       void main()
       {
           ProcessDocument(...);
       }
     
       //! true - Документ проведен - стоп, false - не проведен.
       bool ProcessWaitingFlag(/*тут входные параметры*/)
       {
          if(!Receive.inf || ReceiveIsFullExceed)
          {
             CreateNecessityFlag();
             return FinalizeDocument();
          }
     
          if(ReceiveIsExceedFor)
          {
             SendRequest();
             CreateFlag();
             return true;
          }
     
          Receive();
          if ( isRefresh )
          {
             CreateAvailabilityFlag();
             return FinalizeDocument(true, true, true);
          }
     
          return true;
     
       }
     
       //! true - Документ проведен - стоп, false - не проведен.
       bool PreFinalizeDocument(/*тут входные параметры*/)
       {
          if(isNecessityFlag)
          {
             SendRequest();
             CreateFlag();
             return true;
          }
     
          if(isWaintingAnswerFlag)
          {
             if(! ReceiveAnswer() ) return true;
     
             if(Answer())
             {
                CreateAvailabilityFlag();
                return FinalizeDocument();
             }
     
             SendRequest();
             CreateFlag();
             return true;
          }
     
          return ProcessWaitingFlag();
       }
     
       void ProcessDocument(/*тут входные параметры*/)
       {
          bool isStop = false;
          while(true)
          {
             if(isPhase)
             {
                if(isNecessitySend)
                {
                   SendRequest();
                   break;
                }
     
                if(isWaitingFlag)
                   break;
     
                if(isNeedFinalize)
                   isStop = FinalizeDocument(true, true, true);
                else
                   isStop = PreFinalizeDocument(/*передаем параметры*/);
             }
             else
             {
                isStop = FinalizeDocument(true, true, true);
             }
     
             if(isStop)
                break;
          }
       }
     
       //! true - Документ проведен - стоп, false - не проведен.
       bool FinalizeDocument(bool isExecutionPhase, bool isBuildingFlag, bool isExecutionFlag)
       {
          if(!isExecutionPhase) return true;
     
          if(isBuildingFlag)
          {
             CreateRequest();
             return false;
          }
     
          if( isExecutionFlag )
             Execute();
     
          return true;
       }
     
    private:
       //! Передать запрос.
       void SendRequest(){}
     
       //! Сформировать флаг
       void CreateFlag(){}
     
       //! Принять ответ
       bool ReceiveAnswer(){}
     
       //! Ответ
       bool Answer(){}
     
       //! Создать флаг необходимости
       void CreateNecessityFlag(){}
     
       //! принять
       void Receive(){}
     
       //! Сформировать флаг готовности
       void CreateAvailabilityFlag(){}
     
       //! Проведение
       void Execute(){}
     
       void CreateRequest(){}
    };


И выложи свой пример с goto.

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: ЫукпШ 14.04.15, 12:50
Цитата JoeUser @
Если ты такой правильный, хочешь реального примера - прилагаю сию БС, давай накидай мне хотя-бы схематично без goto. Посмотрим на понятность тобою написанного. Если о5 начнешь про корабли, которые упорно бороздят ... станет все понятно.

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

Это вопрос из серии "Один дурак сумеет задать вопросов больше, чем 10 мудрецов сумеют ответить".
-----
Т.е. важнейшим вопросом в этой истории является кто именно составил эту блок-схему ?
Но даже реализуя именно эту блок-схему без goto всё будет хорошо.
Это просто аксиома (для практических применений) и в доказательствах не нуждается.
Так же как и то, что goto-программиста можно со спокойной совестью увольнять, не вдаваясь
в дальнейшие дискуссии. Он будет постоянно притягивать беды и неудачи.
-----
Вообще это гнусный полемический приёмчик - "вот сделай то и это, а я буду давать тебе оценки".

Это сообщение было перенесено сюда или объединено из темы "Использование глобальных const char*"

Автор: Qraizer 14.04.15, 15:26
Обе схемы реализуются конечным автоматом, JoeUser. Патернов реализации КА великое множество, но что-то я не припомню среди них, основанного на goto. На switch помню, был такой.

P.S. Лично я к goto отношусь либерально. Был бы он не нужен, был бы выпилен из языка, однако не выпилен. Я приветствую goto там, где он к месту. Изначально неструктурный алгоритм, будучи переделан под структурную парадигму, нередко будет только хуже во многих отношениях, если не по всех: поизводительность, ясность, стоимость. В том примере кода, с которого началась сегодняшняя полемика, goto не внёс ничего из указанного.

Автор: JoeUser 14.04.15, 16:19
Цитата KILLER @
И выложи свой пример с goto.

Как-то, так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // тут объявление флагов ...
     
    // Фаза обмена инфой ---------------------------------------------------------------------------
    bool FuncExchange() {
      if (!isPhaseE) return true;                                // - улетаем на фазу обновления
      if (isNecessitySend) { SendRequest(); return false; }      // - передаем запрос, завершаем
      if (isWaitingFlag) return false;                           // - пока в ожидании, завершаем
      if (isExecutionPhase) return true;                         // - улетаем на фазу обновления
      if (isNecessityFlag) {                                     // - создаем запрос
        CreateRequest();                                         //   +
        CreateWaitingFlag();                                     //   флаг ожидания ответа
        return false;                                            //   завершаем работу
      }                                                          //
      if (isWaitAnswerFlag) goto LabelRecv;                      // - улетаем принимать ответ
      if (!isReceiveInfExist || isReceiveInfOverdue) {           // - если Recive.Inf отсутствует
        CreateNecessityFlag();                                   //   или просрочен до возможности
        return true;                                             //   создаем флаг необх.обновления
      }                                                          //   и улетаем на обновление
      if (isReceiveInfOverdueFinish) goto LabelRecovery;         // - Recive.Inf просрочен совсем - вызов сервисного инженера
      Receive();                                                 // - получение ответа
      if (!isUpdateReceived) return false;                       // - обновлений нет, завершение работы
      goto LabelReady;                                           // - обновления есть, формируем флаг готовности
      LabelRecv:     // >>> прием ответа >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      ReceiveAnswer();                                           // - получаем ответ
      if (!isAnsverReceived) return false;                       // - ответ не принят, выходим
      if (!isAnsverTrue) goto LabelRecovery;                     // - ответ неверный, нужно восстановление
      LabelReady:    // >>> формирование флага готовности >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      isExecutionFlag = true;                                    // - формируем флаг готовности
      return true;                                               // - уходим на обновление
      LabelRecovery: // >>> формирование флага сервисного обслуживания >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      CreateFlag();                                              // - создаем флаг на сервисное обслуживание
      return false;                                              // - завершаем работу  
    }
     
    // Фаза проведения обновлений ------------------------------------------------------------------
    bool FuncUpdates() {
      if (!isPhaseB)        return false;                        // фазы обновления нет, выход
      if (isBuildingFlag)   { CreateRequest(); return true; }    // формирование запроса и на фазу обмена
      if (isExecutionFlag)  Execute();                           // обновление если возможно
      return false;                                              // нужно завершить работу!
    }
     
    // Главная процедура ---------------------------------------------------------------------------
    int main() {
      while (FuncExchange() && FuncUpdates());
    }


Но все равно - это не тепичный алгоритм для GOTO, нет возвратов в ветви предыдущих вычислений. Много времени прошло с момента написания этого "робота", помницца и не все реализовано было (как-то, зависание на время формирования ответа серверной частью, возвраты от пустого ответа, на повторный прием ответа и пр.). Тем не менее - в том что я написал, ИМХО, "чтение" кода и сопоставление с блок-схемой проще, нежели в твоих разбиениях на области в виде функций. Без паллитры и карандаша с бумагой не разбересся. У меня "что видно, то и написано".

Автор: KILLER 14.04.15, 16:58
Цитата JoeUser @
Тем не менее - в том что я написал, ИМХО, "чтение" кода и сопоставление с блок-схемой проще, нежели в твоих разбиениях на области в виде функций. Без паллитры и карандаша с бумагой не разбересся. У меня "что видно, то и написано".

Я вообще не понимаю что у тебя происходит в коде, как только мы доходим до goto, тупо по коду не видно куда мы дальше попадем и приходитсся глазами читать. Мой код - читается на ура с входной точки и до конца. По крайней мере я проблем вообще никаких не испытываю с чтением своего кода. В твоем хрен ту метку найдешь глазами. В общем в очередной раз убеждаюсь, что за goto нужно отрывать руки.

Автор: JoeUser 14.04.15, 17:10
Цитата KILLER @
что за goto нужно отрывать руки

Так всех кодеров на ASM'е поколечишь :lol:

Автор: JoeUser 14.04.15, 17:31
Цитата Qraizer @
но что-то я не припомню среди них, основанного на goto

За паттерны не скажу, а вот за реализации запросто и сходу. Пример:

1) Создаем тестовый файлик example.l:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    %{
    #include <stdio.h>
    %}
     
    %%
    stop printf("Stop command received\n");
    start printf("Start command received\n");
    %%.

2) компилячим

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    flex example.l

Скрытый текст
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #line 3 "lex.yy.c"
     
    #define  YY_INT_ALIGNED short int
     
    /* A lexical scanner generated by flex */
     
    #define FLEX_SCANNER
    #define YY_FLEX_MAJOR_VERSION 2
    #define YY_FLEX_MINOR_VERSION 5
    #define YY_FLEX_SUBMINOR_VERSION 39
    #if YY_FLEX_SUBMINOR_VERSION > 0
    #define FLEX_BETA
    #endif
     
    /* First, we deal with  platform-specific or compiler-specific issues. */
     
    /* begin standard C headers. */
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
     
    /* end standard C headers. */
     
    /* flex integer type definitions */
     
    #ifndef FLEXINT_H
    #define FLEXINT_H
     
    /* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
     
    #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
     
    /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
     * if you want the limit (max/min) macros for int types.
     */
    #ifndef __STDC_LIMIT_MACROS
    #define __STDC_LIMIT_MACROS 1
    #endif
     
    #include <inttypes.h>
    typedef int8_t flex_int8_t;
    typedef uint8_t flex_uint8_t;
    typedef int16_t flex_int16_t;
    typedef uint16_t flex_uint16_t;
    typedef int32_t flex_int32_t;
    typedef uint32_t flex_uint32_t;
    #else
    typedef signed char flex_int8_t;
    typedef short int flex_int16_t;
    typedef int flex_int32_t;
    typedef unsigned char flex_uint8_t;
    typedef unsigned short int flex_uint16_t;
    typedef unsigned int flex_uint32_t;
     
    /* Limits of integral types. */
    #ifndef INT8_MIN
    #define INT8_MIN               (-128)
    #endif
    #ifndef INT16_MIN
    #define INT16_MIN              (-32767-1)
    #endif
    #ifndef INT32_MIN
    #define INT32_MIN              (-2147483647-1)
    #endif
    #ifndef INT8_MAX
    #define INT8_MAX               (127)
    #endif
    #ifndef INT16_MAX
    #define INT16_MAX              (32767)
    #endif
    #ifndef INT32_MAX
    #define INT32_MAX              (2147483647)
    #endif
    #ifndef UINT8_MAX
    #define UINT8_MAX              (255U)
    #endif
    #ifndef UINT16_MAX
    #define UINT16_MAX             (65535U)
    #endif
    #ifndef UINT32_MAX
    #define UINT32_MAX             (4294967295U)
    #endif
     
    #endif /* ! C99 */
     
    #endif /* ! FLEXINT_H */
     
    #ifdef __cplusplus
     
    /* The "const" storage-class-modifier is valid. */
    #define YY_USE_CONST
     
    #else   /* ! __cplusplus */
     
    /* C99 requires __STDC__ to be defined as 1. */
    #if defined (__STDC__)
     
    #define YY_USE_CONST
     
    #endif  /* defined (__STDC__) */
    #endif  /* ! __cplusplus */
     
    #ifdef YY_USE_CONST
    #define yyconst const
    #else
    #define yyconst
    #endif
     
    /* Returned upon end-of-file. */
    #define YY_NULL 0
     
    /* Promotes a possibly negative, possibly signed char to an unsigned
     * integer for use as an array index.  If the signed char is negative,
     * we want to instead treat it as an 8-bit unsigned char, hence the
     * double cast.
     */
    #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
     
    /* Enter a start condition.  This macro really ought to take a parameter,
     * but we do it the disgusting crufty way forced on us by the ()-less
     * definition of BEGIN.
     */
    #define BEGIN (yy_start) = 1 + 2 *
     
    /* Translate the current start state into a value that can be later handed
     * to BEGIN to return to the state.  The YYSTATE alias is for lex
     * compatibility.
     */
    #define YY_START (((yy_start) - 1) / 2)
    #define YYSTATE YY_START
     
    /* Action number for EOF rule of a given start state. */
    #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
     
    /* Special action meaning "start processing a new file". */
    #define YY_NEW_FILE yyrestart(yyin  )
     
    #define YY_END_OF_BUFFER_CHAR 0
     
    /* Size of default input buffer. */
    #ifndef YY_BUF_SIZE
    #define YY_BUF_SIZE 16384
    #endif
     
    /* The state buf must be large enough to hold one state per character in the main buffer.
     */
    #define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
     
    #ifndef YY_TYPEDEF_YY_BUFFER_STATE
    #define YY_TYPEDEF_YY_BUFFER_STATE
    typedef struct yy_buffer_state *YY_BUFFER_STATE;
    #endif
     
    #ifndef YY_TYPEDEF_YY_SIZE_T
    #define YY_TYPEDEF_YY_SIZE_T
    typedef size_t yy_size_t;
    #endif
     
    extern yy_size_t yyleng;
     
    extern FILE *yyin, *yyout;
     
    #define EOB_ACT_CONTINUE_SCAN 0
    #define EOB_ACT_END_OF_FILE 1
    #define EOB_ACT_LAST_MATCH 2
     
        #define YY_LESS_LINENO(n)
        #define YY_LINENO_REWIND_TO(ptr)
        
    /* Return all but the first "n" matched characters back to the input stream. */
    #define yyless(n) \
        do \
            { \
            /* Undo effects of setting up yytext. */ \
            int yyless_macro_arg = (n); \
            YY_LESS_LINENO(yyless_macro_arg);\
            *yy_cp = (yy_hold_char); \
            YY_RESTORE_YY_MORE_OFFSET \
            (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
            YY_DO_BEFORE_ACTION; /* set up yytext again */ \
            } \
        while ( 0 )
     
    #define unput(c) yyunput( c, (yytext_ptr)  )
     
    #ifndef YY_STRUCT_YY_BUFFER_STATE
    #define YY_STRUCT_YY_BUFFER_STATE
    struct yy_buffer_state
        {
        FILE *yy_input_file;
     
        char *yy_ch_buf;        /* input buffer */
        char *yy_buf_pos;       /* current position in input buffer */
     
        /* Size of input buffer in bytes, not including room for EOB
         * characters.
         */
        yy_size_t yy_buf_size;
     
        /* Number of characters read into yy_ch_buf, not including EOB
         * characters.
         */
        yy_size_t yy_n_chars;
     
        /* Whether we "own" the buffer - i.e., we know we created it,
         * and can realloc() it to grow it, and should free() it to
         * delete it.
         */
        int yy_is_our_buffer;
     
        /* Whether this is an "interactive" input source; if so, and
         * if we're using stdio for input, then we want to use getc()
         * instead of fread(), to make sure we stop fetching input after
         * each newline.
         */
        int yy_is_interactive;
     
        /* Whether we're considered to be at the beginning of a line.
         * If so, '^' rules will be active on the next match, otherwise
         * not.
         */
        int yy_at_bol;
     
        int yy_bs_lineno; /**< The line count. */
        int yy_bs_column; /**< The column count. */
        
        /* Whether to try to fill the input buffer when we reach the
         * end of it.
         */
        int yy_fill_buffer;
     
        int yy_buffer_status;
     
    #define YY_BUFFER_NEW 0
    #define YY_BUFFER_NORMAL 1
        /* When an EOF's been seen but there's still some text to process
         * then we mark the buffer as YY_EOF_PENDING, to indicate that we
         * shouldn't try reading from the input source any more.  We might
         * still have a bunch of tokens to match, though, because of
         * possible backing-up.
         *
         * When we actually see the EOF, we change the status to "new"
         * (via yyrestart()), so that the user can continue scanning by
         * just pointing yyin at a new input file.
         */
    #define YY_BUFFER_EOF_PENDING 2
     
        };
    #endif /* !YY_STRUCT_YY_BUFFER_STATE */
     
    /* Stack of input buffers. */
    static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
    static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
    static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
     
    /* We provide macros for accessing buffer states in case in the
     * future we want to put the buffer states in a more general
     * "scanner state".
     *
     * Returns the top of the stack, or NULL.
     */
    #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
                              ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
                              : NULL)
     
    /* Same as previous macro, but useful when we know that the buffer stack is not
     * NULL or when we need an lvalue. For internal use only.
     */
    #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
     
    /* yy_hold_char holds the character lost when yytext is formed. */
    static char yy_hold_char;
    static yy_size_t yy_n_chars;        /* number of characters read into yy_ch_buf */
    yy_size_t yyleng;
     
    /* Points to current character in buffer. */
    static char *yy_c_buf_p = (char *) 0;
    static int yy_init = 0;     /* whether we need to initialize */
    static int yy_start = 0;    /* start state number */
     
    /* Flag which is used to allow yywrap()'s to do buffer switches
     * instead of setting up a fresh yyin.  A bit of a hack ...
     */
    static int yy_did_buffer_switch_on_eof;
     
    void yyrestart (FILE *input_file  );
    void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
    YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
    void yy_delete_buffer (YY_BUFFER_STATE b  );
    void yy_flush_buffer (YY_BUFFER_STATE b  );
    void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
    void yypop_buffer_state (void );
     
    static void yyensure_buffer_stack (void );
    static void yy_load_buffer_state (void );
    static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
     
    #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
     
    YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
    YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
    YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len  );
     
    void *yyalloc (yy_size_t  );
    void *yyrealloc (void *,yy_size_t  );
    void yyfree (void *  );
     
    #define yy_new_buffer yy_create_buffer
     
    #define yy_set_interactive(is_interactive) \
        { \
        if ( ! YY_CURRENT_BUFFER ){ \
            yyensure_buffer_stack (); \
            YY_CURRENT_BUFFER_LVALUE =    \
                yy_create_buffer(yyin,YY_BUF_SIZE ); \
        } \
        YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
        }
     
    #define yy_set_bol(at_bol) \
        { \
        if ( ! YY_CURRENT_BUFFER ){\
            yyensure_buffer_stack (); \
            YY_CURRENT_BUFFER_LVALUE =    \
                yy_create_buffer(yyin,YY_BUF_SIZE ); \
        } \
        YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
        }
     
    #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
     
    /* Begin user sect3 */
     
    typedef unsigned char YY_CHAR;
     
    FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
     
    typedef int yy_state_type;
     
    extern int yylineno;
     
    int yylineno = 1;
     
    extern char *yytext;
    #define yytext_ptr yytext
     
    static yy_state_type yy_get_previous_state (void );
    static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
    static int yy_get_next_buffer (void );
    static void yy_fatal_error (yyconst char msg[]  );
     
    /* Done after the current pattern has been matched and before the
     * corresponding action - sets up yytext.
     */
    #define YY_DO_BEFORE_ACTION \
        (yytext_ptr) = yy_bp; \
        yyleng = (size_t) (yy_cp - yy_bp); \
        (yy_hold_char) = *yy_cp; \
        *yy_cp = '\0'; \
        (yy_c_buf_p) = yy_cp;
     
    #define YY_NUM_RULES 3
    #define YY_END_OF_BUFFER 4
    /* This struct is not used in this scanner,
       but its presence is necessary. */
    struct yy_trans_info
        {
        flex_int32_t yy_verify;
        flex_int32_t yy_nxt;
        };
    static yyconst flex_int16_t yy_accept[13] =
        {   0,
            0,    0,    4,    3,    3,    0,    0,    0,    0,    1,
            2,    0
        } ;
     
    static yyconst flex_int32_t yy_ec[256] =
        {   0,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    2,    1,    1,    1,
     
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            3,    4,    1,    5,    6,    7,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
     
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1
        } ;
     
    static yyconst flex_int32_t yy_meta[8] =
        {   0,
            1,    1,    1,    1,    1,    1,    1
        } ;
     
    static yyconst flex_int16_t yy_base[14] =
        {   0,
            7,    6,   11,   14,    3,    0,    4,    4,    0,   14,
           14,   14,    0
        } ;
     
    static yyconst flex_int16_t yy_def[14] =
        {   0,
           13,   13,   12,   12,   12,   12,   12,   12,   12,   12,
           12,    0,   12
        } ;
     
    static yyconst flex_int16_t yy_nxt[22] =
        {   0,
            4,    7,    8,   12,   12,   12,   11,   10,    9,    6,
           12,    5,    5,    3,   12,   12,   12,   12,   12,   12,
           12
        } ;
     
    static yyconst flex_int16_t yy_chk[22] =
        {   0,
           13,    6,    6,    0,    0,    0,    9,    8,    7,    5,
            3,    2,    1,   12,   12,   12,   12,   12,   12,   12,
           12
        } ;
     
    static yy_state_type yy_last_accepting_state;
    static char *yy_last_accepting_cpos;
     
    extern int yy_flex_debug;
    int yy_flex_debug = 0;
     
    /* The intent behind this definition is that it'll catch
     * any uses of REJECT which flex missed.
     */
    #define REJECT reject_used_but_not_detected
    #define yymore() yymore_used_but_not_detected
    #define YY_MORE_ADJ 0
    #define YY_RESTORE_YY_MORE_OFFSET
    char *yytext;
    #line 1 "example.l"
    #line 2 "example.l"
    #include <stdio.h>
    #line 460 "lex.yy.c"
     
    #define INITIAL 0
     
    #ifndef YY_NO_UNISTD_H
    /* Special case for "unistd.h", since it is non-ANSI. We include it way
     * down here because we want the user's section 1 to have been scanned first.
     * The user has a chance to override it with an option.
     */
    #include <unistd.h>
    #endif
     
    #ifndef YY_EXTRA_TYPE
    #define YY_EXTRA_TYPE void *
    #endif
     
    static int yy_init_globals (void );
     
    /* Accessor methods to globals.
       These are made visible to non-reentrant scanners for convenience. */
     
    int yylex_destroy (void );
     
    int yyget_debug (void );
     
    void yyset_debug (int debug_flag  );
     
    YY_EXTRA_TYPE yyget_extra (void );
     
    void yyset_extra (YY_EXTRA_TYPE user_defined  );
     
    FILE *yyget_in (void );
     
    void yyset_in  (FILE * in_str  );
     
    FILE *yyget_out (void );
     
    void yyset_out  (FILE * out_str  );
     
    yy_size_t yyget_leng (void );
     
    char *yyget_text (void );
     
    int yyget_lineno (void );
     
    void yyset_lineno (int line_number  );
     
    /* Macros after this point can all be overridden by user definitions in
     * section 1.
     */
     
    #ifndef YY_SKIP_YYWRAP
    #ifdef __cplusplus
    extern "C" int yywrap (void );
    #else
    extern int yywrap (void );
    #endif
    #endif
     
        static void yyunput (int c,char *buf_ptr  );
        
    #ifndef yytext_ptr
    static void yy_flex_strncpy (char *,yyconst char *,int );
    #endif
     
    #ifdef YY_NEED_STRLEN
    static int yy_flex_strlen (yyconst char * );
    #endif
     
    #ifndef YY_NO_INPUT
     
    #ifdef __cplusplus
    static int yyinput (void );
    #else
    static int input (void );
    #endif
     
    #endif
     
    /* Amount of stuff to slurp up with each read. */
    #ifndef YY_READ_BUF_SIZE
    #define YY_READ_BUF_SIZE 8192
    #endif
     
    /* Copy whatever the last rule matched to the standard output. */
    #ifndef ECHO
    /* This used to be an fputs(), but since the string might contain NUL's,
     * we now use fwrite().
     */
    #define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
    #endif
     
    /* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
     * is returned in "result".
     */
    #ifndef YY_INPUT
    #define YY_INPUT(buf,result,max_size) \
        if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
            { \
            int c = '*'; \
            size_t n; \
            for ( n = 0; n < max_size && \
                     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
                buf[n] = (char) c; \
            if ( c == '\n' ) \
                buf[n++] = (char) c; \
            if ( c == EOF && ferror( yyin ) ) \
                YY_FATAL_ERROR( "input in flex scanner failed" ); \
            result = n; \
            } \
        else \
            { \
            errno=0; \
            while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
                { \
                if( errno != EINTR) \
                    { \
                    YY_FATAL_ERROR( "input in flex scanner failed" ); \
                    break; \
                    } \
                errno=0; \
                clearerr(yyin); \
                } \
            }\
    \
     
    #endif
     
    /* No semi-colon after return; correct usage is to write "yyterminate();" -
     * we don't want an extra ';' after the "return" because that will cause
     * some compilers to complain about unreachable statements.
     */
    #ifndef yyterminate
    #define yyterminate() return YY_NULL
    #endif
     
    /* Number of entries by which start-condition stack grows. */
    #ifndef YY_START_STACK_INCR
    #define YY_START_STACK_INCR 25
    #endif
     
    /* Report a fatal error. */
    #ifndef YY_FATAL_ERROR
    #define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
    #endif
     
    /* end tables serialization structures and prototypes */
     
    /* Default declaration of generated scanner - a define so the user can
     * easily add parameters.
     */
    #ifndef YY_DECL
    #define YY_DECL_IS_OURS 1
     
    extern int yylex (void);
     
    #define YY_DECL int yylex (void)
    #endif /* !YY_DECL */
     
    /* Code executed at the beginning of each rule, after yytext and yyleng
     * have been set up.
     */
    #ifndef YY_USER_ACTION
    #define YY_USER_ACTION
    #endif
     
    /* Code executed at the end of each rule. */
    #ifndef YY_BREAK
    #define YY_BREAK break;
    #endif
     
    #define YY_RULE_SETUP \
        YY_USER_ACTION
     
    /** The main scanner function which does all the work.
     */
    YY_DECL
    {
        register yy_state_type yy_current_state;
        register char *yy_cp, *yy_bp;
        register int yy_act;
        
        if ( !(yy_init) )
            {
            (yy_init) = 1;
     
    #ifdef YY_USER_INIT
            YY_USER_INIT;
    #endif
     
            if ( ! (yy_start) )
                (yy_start) = 1; /* first start state */
     
            if ( ! yyin )
                yyin = stdin;
     
            if ( ! yyout )
                yyout = stdout;
     
            if ( ! YY_CURRENT_BUFFER ) {
                yyensure_buffer_stack ();
                YY_CURRENT_BUFFER_LVALUE =
                    yy_create_buffer(yyin,YY_BUF_SIZE );
            }
     
            yy_load_buffer_state( );
            }
     
        {
    #line 5 "example.l"
     
    #line 671 "lex.yy.c"
     
        while ( 1 )     /* loops until end-of-file is reached */
            {
            yy_cp = (yy_c_buf_p);
     
            /* Support of yytext. */
            *yy_cp = (yy_hold_char);
     
            /* yy_bp points to the position in yy_ch_buf of the start of
             * the current run.
             */
            yy_bp = yy_cp;
     
            yy_current_state = (yy_start);
    yy_match:
            do
                {
                register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
                if ( yy_accept[yy_current_state] )
                    {
                    (yy_last_accepting_state) = yy_current_state;
                    (yy_last_accepting_cpos) = yy_cp;
                    }
                while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                    {
                    yy_current_state = (int) yy_def[yy_current_state];
                    if ( yy_current_state >= 13 )
                        yy_c = yy_meta[(unsigned int) yy_c];
                    }
                yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
                ++yy_cp;
                }
            while ( yy_base[yy_current_state] != 14 );
     
    yy_find_action:
            yy_act = yy_accept[yy_current_state];
            if ( yy_act == 0 )
                { /* have to back up */
                yy_cp = (yy_last_accepting_cpos);
                yy_current_state = (yy_last_accepting_state);
                yy_act = yy_accept[yy_current_state];
                }
     
            YY_DO_BEFORE_ACTION;
     
    do_action:  /* This label is used only to access EOF actions. */
     
            switch ( yy_act )
        { /* beginning of action switch */
                case 0: /* must back up */
                /* undo the effects of YY_DO_BEFORE_ACTION */
                *yy_cp = (yy_hold_char);
                yy_cp = (yy_last_accepting_cpos);
                yy_current_state = (yy_last_accepting_state);
                goto yy_find_action;
     
    case 1:
    YY_RULE_SETUP
    #line 6 "example.l"
    printf("Stop command received\n");
        YY_BREAK
    case 2:
    YY_RULE_SETUP
    #line 7 "example.l"
    printf("Start command received\n");
        YY_BREAK
    case 3:
    YY_RULE_SETUP
    #line 8 "example.l"
    ECHO;
        YY_BREAK
    #line 743 "lex.yy.c"
    case YY_STATE_EOF(INITIAL):
        yyterminate();
     
        case YY_END_OF_BUFFER:
            {
            /* Amount of text matched not including the EOB char. */
            int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
     
            /* Undo the effects of YY_DO_BEFORE_ACTION. */
            *yy_cp = (yy_hold_char);
            YY_RESTORE_YY_MORE_OFFSET
     
            if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
                {
                /* We're scanning a new file or input source.  It's
                 * possible that this happened because the user
                 * just pointed yyin at a new source and called
                 * yylex().  If so, then we have to assure
                 * consistency between YY_CURRENT_BUFFER and our
                 * globals.  Here is the right place to do so, because
                 * this is the first action (other than possibly a
                 * back-up) that will match for the new input source.
                 */
                (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
                YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
                YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
                }
     
            /* Note that here we test for yy_c_buf_p "<=" to the position
             * of the first EOB in the buffer, since yy_c_buf_p will
             * already have been incremented past the NUL character
             * (since all states make transitions on EOB to the
             * end-of-buffer state).  Contrast this with the test
             * in input().
             */
            if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
                { /* This was really a NUL. */
                yy_state_type yy_next_state;
     
                (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
     
                yy_current_state = yy_get_previous_state(  );
     
                /* Okay, we're now positioned to make the NUL
                 * transition.  We couldn't have
                 * yy_get_previous_state() go ahead and do it
                 * for us because it doesn't know how to deal
                 * with the possibility of jamming (and we don't
                 * want to build jamming into it because then it
                 * will run more slowly).
                 */
     
                yy_next_state = yy_try_NUL_trans( yy_current_state );
     
                yy_bp = (yytext_ptr) + YY_MORE_ADJ;
     
                if ( yy_next_state )
                    {
                    /* Consume the NUL. */
                    yy_cp = ++(yy_c_buf_p);
                    yy_current_state = yy_next_state;
                    goto yy_match;
                    }
     
                else
                    {
                    yy_cp = (yy_c_buf_p);
                    goto yy_find_action;
                    }
                }
     
            else switch ( yy_get_next_buffer(  ) )
                {
                case EOB_ACT_END_OF_FILE:
                    {
                    (yy_did_buffer_switch_on_eof) = 0;
     
                    if ( yywrap( ) )
                        {
                        /* Note: because we've taken care in
                         * yy_get_next_buffer() to have set up
                         * yytext, we can now set up
                         * yy_c_buf_p so that if some total
                         * hoser (like flex itself) wants to
                         * call the scanner after we return the
                         * YY_NULL, it'll still work - another
                         * YY_NULL will get returned.
                         */
                        (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
     
                        yy_act = YY_STATE_EOF(YY_START);
                        goto do_action;
                        }
     
                    else
                        {
                        if ( ! (yy_did_buffer_switch_on_eof) )
                            YY_NEW_FILE;
                        }
                    break;
                    }
     
                case EOB_ACT_CONTINUE_SCAN:
                    (yy_c_buf_p) =
                        (yytext_ptr) + yy_amount_of_matched_text;
     
                    yy_current_state = yy_get_previous_state(  );
     
                    yy_cp = (yy_c_buf_p);
                    yy_bp = (yytext_ptr) + YY_MORE_ADJ;
                    goto yy_match;
     
                case EOB_ACT_LAST_MATCH:
                    (yy_c_buf_p) =
                    &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
     
                    yy_current_state = yy_get_previous_state(  );
     
                    yy_cp = (yy_c_buf_p);
                    yy_bp = (yytext_ptr) + YY_MORE_ADJ;
                    goto yy_find_action;
                }
            break;
            }
     
        default:
            YY_FATAL_ERROR(
                "fatal flex scanner internal error--no action found" );
        } /* end of action switch */
            } /* end of scanning one token */
        } /* end of user's declarations */
    } /* end of yylex */
     
    /* yy_get_next_buffer - try to read in a new buffer
     *
     * Returns a code representing an action:
     *  EOB_ACT_LAST_MATCH -
     *  EOB_ACT_CONTINUE_SCAN - continue scanning from current position
     *  EOB_ACT_END_OF_FILE - end of file
     */
    static int yy_get_next_buffer (void)
    {
            register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
        register char *source = (yytext_ptr);
        register int number_to_move, i;
        int ret_val;
     
        if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
            YY_FATAL_ERROR(
            "fatal flex scanner internal error--end of buffer missed" );
     
        if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
            { /* Don't try to fill the buffer, so this is an EOF. */
            if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
                {
                /* We matched a single character, the EOB, so
                 * treat this as a final EOF.
                 */
                return EOB_ACT_END_OF_FILE;
                }
     
            else
                {
                /* We matched some text prior to the EOB, first
                 * process it.
                 */
                return EOB_ACT_LAST_MATCH;
                }
            }
     
        /* Try to read more data. */
     
        /* First move last chars to start of buffer. */
        number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
     
        for ( i = 0; i < number_to_move; ++i )
            *(dest++) = *(source++);
     
        if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
            /* don't do the read, it's not guaranteed to return an EOF,
             * just force an EOF
             */
            YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
     
        else
            {
                yy_size_t num_to_read =
                YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
     
            while ( num_to_read <= 0 )
                { /* Not enough room in the buffer - grow it. */
     
                /* just a shorter name for the current buffer */
                YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
     
                int yy_c_buf_p_offset =
                    (int) ((yy_c_buf_p) - b->yy_ch_buf);
     
                if ( b->yy_is_our_buffer )
                    {
                    yy_size_t new_size = b->yy_buf_size * 2;
     
                    if ( new_size <= 0 )
                        b->yy_buf_size += b->yy_buf_size / 8;
                    else
                        b->yy_buf_size *= 2;
     
                    b->yy_ch_buf = (char *)
                        /* Include room in for 2 EOB chars. */
                        yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
                    }
                else
                    /* Can't grow it, we don't own it. */
                    b->yy_ch_buf = 0;
     
                if ( ! b->yy_ch_buf )
                    YY_FATAL_ERROR(
                    "fatal error - scanner input buffer overflow" );
     
                (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
     
                num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
                            number_to_move - 1;
     
                }
     
            if ( num_to_read > YY_READ_BUF_SIZE )
                num_to_read = YY_READ_BUF_SIZE;
     
            /* Read in more data. */
            YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
                (yy_n_chars), num_to_read );
     
            YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
            }
     
        if ( (yy_n_chars) == 0 )
            {
            if ( number_to_move == YY_MORE_ADJ )
                {
                ret_val = EOB_ACT_END_OF_FILE;
                yyrestart(yyin  );
                }
     
            else
                {
                ret_val = EOB_ACT_LAST_MATCH;
                YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
                    YY_BUFFER_EOF_PENDING;
                }
            }
     
        else
            ret_val = EOB_ACT_CONTINUE_SCAN;
     
        if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
            /* Extend the array by 50%, plus the number we really need. */
            yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
            YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
            if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
                YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
        }
     
        (yy_n_chars) += number_to_move;
        YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
        YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
     
        (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
     
        return ret_val;
    }
     
    /* yy_get_previous_state - get the state just before the EOB char was reached */
     
        static yy_state_type yy_get_previous_state (void)
    {
        register yy_state_type yy_current_state;
        register char *yy_cp;
        
        yy_current_state = (yy_start);
     
        for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
            {
            register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
            if ( yy_accept[yy_current_state] )
                {
                (yy_last_accepting_state) = yy_current_state;
                (yy_last_accepting_cpos) = yy_cp;
                }
            while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                {
                yy_current_state = (int) yy_def[yy_current_state];
                if ( yy_current_state >= 13 )
                    yy_c = yy_meta[(unsigned int) yy_c];
                }
            yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
            }
     
        return yy_current_state;
    }
     
    /* yy_try_NUL_trans - try to make a transition on the NUL character
     *
     * synopsis
     *  next_state = yy_try_NUL_trans( current_state );
     */
        static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
    {
        register int yy_is_jam;
            register char *yy_cp = (yy_c_buf_p);
     
        register YY_CHAR yy_c = 1;
        if ( yy_accept[yy_current_state] )
            {
            (yy_last_accepting_state) = yy_current_state;
            (yy_last_accepting_cpos) = yy_cp;
            }
        while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
            {
            yy_current_state = (int) yy_def[yy_current_state];
            if ( yy_current_state >= 13 )
                yy_c = yy_meta[(unsigned int) yy_c];
            }
        yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
        yy_is_jam = (yy_current_state == 12);
     
            return yy_is_jam ? 0 : yy_current_state;
    }
     
        static void yyunput (int c, register char * yy_bp )
    {
        register char *yy_cp;
        
        yy_cp = (yy_c_buf_p);
     
        /* undo effects of setting up yytext */
        *yy_cp = (yy_hold_char);
     
        if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
            { /* need to shift things up to make room */
            /* +2 for EOB chars. */
            register yy_size_t number_to_move = (yy_n_chars) + 2;
            register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
                        YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
            register char *source =
                    &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
     
            while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
                *--dest = *--source;
     
            yy_cp += (int) (dest - source);
            yy_bp += (int) (dest - source);
            YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
                (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
     
            if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
                YY_FATAL_ERROR( "flex scanner push-back overflow" );
            }
     
        *--yy_cp = (char) c;
     
        (yytext_ptr) = yy_bp;
        (yy_hold_char) = *yy_cp;
        (yy_c_buf_p) = yy_cp;
    }
     
    #ifndef YY_NO_INPUT
    #ifdef __cplusplus
        static int yyinput (void)
    #else
        static int input  (void)
    #endif
     
    {
        int c;
        
        *(yy_c_buf_p) = (yy_hold_char);
     
        if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
            {
            /* yy_c_buf_p now points to the character we want to return.
             * If this occurs *before* the EOB characters, then it's a
             * valid NUL; if not, then we've hit the end of the buffer.
             */
            if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
                /* This was really a NUL. */
                *(yy_c_buf_p) = '\0';
     
            else
                { /* need more input */
                yy_size_t offset = (yy_c_buf_p) - (yytext_ptr);
                ++(yy_c_buf_p);
     
                switch ( yy_get_next_buffer(  ) )
                    {
                    case EOB_ACT_LAST_MATCH:
                        /* This happens because yy_g_n_b()
                         * sees that we've accumulated a
                         * token and flags that we need to
                         * try matching the token before
                         * proceeding.  But for input(),
                         * there's no matching to consider.
                         * So convert the EOB_ACT_LAST_MATCH
                         * to EOB_ACT_END_OF_FILE.
                         */
     
                        /* Reset buffer status. */
                        yyrestart(yyin );
     
                        /*FALLTHROUGH*/
     
                    case EOB_ACT_END_OF_FILE:
                        {
                        if ( yywrap( ) )
                            return EOF;
     
                        if ( ! (yy_did_buffer_switch_on_eof) )
                            YY_NEW_FILE;
    #ifdef __cplusplus
                        return yyinput();
    #else
                        return input();
    #endif
                        }
     
                    case EOB_ACT_CONTINUE_SCAN:
                        (yy_c_buf_p) = (yytext_ptr) + offset;
                        break;
                    }
                }
            }
     
        c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
        *(yy_c_buf_p) = '\0';   /* preserve yytext */
        (yy_hold_char) = *++(yy_c_buf_p);
     
        return c;
    }
    #endif  /* ifndef YY_NO_INPUT */
     
    /** Immediately switch to a different input stream.
     * @param input_file A readable stream.
     *
     * @note This function does not reset the start condition to @c INITIAL .
     */
        void yyrestart  (FILE * input_file )
    {
        
        if ( ! YY_CURRENT_BUFFER ){
            yyensure_buffer_stack ();
            YY_CURRENT_BUFFER_LVALUE =
                yy_create_buffer(yyin,YY_BUF_SIZE );
        }
     
        yy_init_buffer(YY_CURRENT_BUFFER,input_file );
        yy_load_buffer_state( );
    }
     
    /** Switch to a different input buffer.
     * @param new_buffer The new input buffer.
     *
     */
        void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
    {
        
        /* TODO. We should be able to replace this entire function body
         * with
         *      yypop_buffer_state();
         *      yypush_buffer_state(new_buffer);
         */
        yyensure_buffer_stack ();
        if ( YY_CURRENT_BUFFER == new_buffer )
            return;
     
        if ( YY_CURRENT_BUFFER )
            {
            /* Flush out information for old buffer. */
            *(yy_c_buf_p) = (yy_hold_char);
            YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
            YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
            }
     
        YY_CURRENT_BUFFER_LVALUE = new_buffer;
        yy_load_buffer_state( );
     
        /* We don't actually know whether we did this switch during
         * EOF (yywrap()) processing, but the only time this flag
         * is looked at is after yywrap() is called, so it's safe
         * to go ahead and always set it.
         */
        (yy_did_buffer_switch_on_eof) = 1;
    }
     
    static void yy_load_buffer_state  (void)
    {
            (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
        (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
        yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
        (yy_hold_char) = *(yy_c_buf_p);
    }
     
    /** Allocate and initialize an input buffer state.
     * @param file A readable stream.
     * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
     *
     * @return the allocated buffer state.
     */
        YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
    {
        YY_BUFFER_STATE b;
        
        b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
        if ( ! b )
            YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
     
        b->yy_buf_size = size;
     
        /* yy_ch_buf has to be 2 characters longer than the size given because
         * we need to put in 2 end-of-buffer characters.
         */
        b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
        if ( ! b->yy_ch_buf )
            YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
     
        b->yy_is_our_buffer = 1;
     
        yy_init_buffer(b,file );
     
        return b;
    }
     
    /** Destroy the buffer.
     * @param b a buffer created with yy_create_buffer()
     *
     */
        void yy_delete_buffer (YY_BUFFER_STATE  b )
    {
        
        if ( ! b )
            return;
     
        if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
            YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
     
        if ( b->yy_is_our_buffer )
            yyfree((void *) b->yy_ch_buf  );
     
        yyfree((void *) b  );
    }
     
    /* Initializes or reinitializes a buffer.
     * This function is sometimes called more than once on the same buffer,
     * such as during a yyrestart() or at EOF.
     */
        static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
     
    {
        int oerrno = errno;
        
        yy_flush_buffer(b );
     
        b->yy_input_file = file;
        b->yy_fill_buffer = 1;
     
        /* If b is the current buffer, then yy_init_buffer was _probably_
         * called from yyrestart() or through yy_get_next_buffer.
         * In that case, we don't want to reset the lineno or column.
         */
        if (b != YY_CURRENT_BUFFER){
            b->yy_bs_lineno = 1;
            b->yy_bs_column = 0;
        }
     
            b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
        
        errno = oerrno;
    }
     
    /** Discard all buffered characters. On the next scan, YY_INPUT will be called.
     * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
     *
     */
        void yy_flush_buffer (YY_BUFFER_STATE  b )
    {
            if ( ! b )
            return;
     
        b->yy_n_chars = 0;
     
        /* We always need two end-of-buffer characters.  The first causes
         * a transition to the end-of-buffer state.  The second causes
         * a jam in that state.
         */
        b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
        b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
     
        b->yy_buf_pos = &b->yy_ch_buf[0];
     
        b->yy_at_bol = 1;
        b->yy_buffer_status = YY_BUFFER_NEW;
     
        if ( b == YY_CURRENT_BUFFER )
            yy_load_buffer_state( );
    }
     
    /** Pushes the new state onto the stack. The new state becomes
     *  the current state. This function will allocate the stack
     *  if necessary.
     *  @param new_buffer The new state.
     *  
     */
    void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
    {
            if (new_buffer == NULL)
            return;
     
        yyensure_buffer_stack();
     
        /* This block is copied from yy_switch_to_buffer. */
        if ( YY_CURRENT_BUFFER )
            {
            /* Flush out information for old buffer. */
            *(yy_c_buf_p) = (yy_hold_char);
            YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
            YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
            }
     
        /* Only push if top exists. Otherwise, replace top. */
        if (YY_CURRENT_BUFFER)
            (yy_buffer_stack_top)++;
        YY_CURRENT_BUFFER_LVALUE = new_buffer;
     
        /* copied from yy_switch_to_buffer. */
        yy_load_buffer_state( );
        (yy_did_buffer_switch_on_eof) = 1;
    }
     
    /** Removes and deletes the top of the stack, if present.
     *  The next element becomes the new top.
     *  
     */
    void yypop_buffer_state (void)
    {
            if (!YY_CURRENT_BUFFER)
            return;
     
        yy_delete_buffer(YY_CURRENT_BUFFER );
        YY_CURRENT_BUFFER_LVALUE = NULL;
        if ((yy_buffer_stack_top) > 0)
            --(yy_buffer_stack_top);
     
        if (YY_CURRENT_BUFFER) {
            yy_load_buffer_state( );
            (yy_did_buffer_switch_on_eof) = 1;
        }
    }
     
    /* Allocates the stack if it does not exist.
     *  Guarantees space for at least one push.
     */
    static void yyensure_buffer_stack (void)
    {
        yy_size_t num_to_alloc;
        
        if (!(yy_buffer_stack)) {
     
            /* First allocation is just for 2 elements, since we don't know if this
             * scanner will even need a stack. We use 2 instead of 1 to avoid an
             * immediate realloc on the next call.
             */
            num_to_alloc = 1;
            (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
                                    (num_to_alloc * sizeof(struct yy_buffer_state*)
                                    );
            if ( ! (yy_buffer_stack) )
                YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
                                      
            memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
                    
            (yy_buffer_stack_max) = num_to_alloc;
            (yy_buffer_stack_top) = 0;
            return;
        }
     
        if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
     
            /* Increase the buffer to prepare for a possible push. */
            int grow_size = 8 /* arbitrary grow size */;
     
            num_to_alloc = (yy_buffer_stack_max) + grow_size;
            (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
                                    ((yy_buffer_stack),
                                    num_to_alloc * sizeof(struct yy_buffer_state*)
                                    );
            if ( ! (yy_buffer_stack) )
                YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
     
            /* zero only the new slots.*/
            memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
            (yy_buffer_stack_max) = num_to_alloc;
        }
    }
     
    /** Setup the input buffer state to scan directly from a user-specified character buffer.
     * @param base the character buffer
     * @param size the size in bytes of the character buffer
     *
     * @return the newly allocated buffer state object.
     */
    YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
    {
        YY_BUFFER_STATE b;
        
        if ( size < 2 ||
             base[size-2] != YY_END_OF_BUFFER_CHAR ||
             base[size-1] != YY_END_OF_BUFFER_CHAR )
            /* They forgot to leave room for the EOB's. */
            return 0;
     
        b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
        if ( ! b )
            YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
     
        b->yy_buf_size = size - 2;  /* "- 2" to take care of EOB's */
        b->yy_buf_pos = b->yy_ch_buf = base;
        b->yy_is_our_buffer = 0;
        b->yy_input_file = 0;
        b->yy_n_chars = b->yy_buf_size;
        b->yy_is_interactive = 0;
        b->yy_at_bol = 1;
        b->yy_fill_buffer = 0;
        b->yy_buffer_status = YY_BUFFER_NEW;
     
        yy_switch_to_buffer(b  );
     
        return b;
    }
     
    /** Setup the input buffer state to scan a string. The next call to yylex() will
     * scan from a @e copy of @a str.
     * @param yystr a NUL-terminated string to scan
     *
     * @return the newly allocated buffer state object.
     * @note If you want to scan bytes that may contain NUL values, then use
     *       yy_scan_bytes() instead.
     */
    YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
    {
        
        return yy_scan_bytes(yystr,strlen(yystr) );
    }
     
    /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
     * scan from a @e copy of @a bytes.
     * @param yybytes the byte buffer to scan
     * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
     *
     * @return the newly allocated buffer state object.
     */
    YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len )
    {
        YY_BUFFER_STATE b;
        char *buf;
        yy_size_t n;
        yy_size_t i;
        
        /* Get memory for full buffer, including space for trailing EOB's. */
        n = _yybytes_len + 2;
        buf = (char *) yyalloc(n  );
        if ( ! buf )
            YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
     
        for ( i = 0; i < _yybytes_len; ++i )
            buf[i] = yybytes[i];
     
        buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
     
        b = yy_scan_buffer(buf,n );
        if ( ! b )
            YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
     
        /* It's okay to grow etc. this buffer, and we should throw it
         * away when we're done.
         */
        b->yy_is_our_buffer = 1;
     
        return b;
    }
     
    #ifndef YY_EXIT_FAILURE
    #define YY_EXIT_FAILURE 2
    #endif
     
    static void yy_fatal_error (yyconst char* msg )
    {
            (void) fprintf( stderr, "%s\n", msg );
        exit( YY_EXIT_FAILURE );
    }
     
    /* Redefine yyless() so it works in section 3 code. */
     
    #undef yyless
    #define yyless(n) \
        do \
            { \
            /* Undo effects of setting up yytext. */ \
            int yyless_macro_arg = (n); \
            YY_LESS_LINENO(yyless_macro_arg);\
            yytext[yyleng] = (yy_hold_char); \
            (yy_c_buf_p) = yytext + yyless_macro_arg; \
            (yy_hold_char) = *(yy_c_buf_p); \
            *(yy_c_buf_p) = '\0'; \
            yyleng = yyless_macro_arg; \
            } \
        while ( 0 )
     
    /* Accessor  methods (get/set functions) to struct members. */
     
    /** Get the current line number.
     *
     */
    int yyget_lineno  (void)
    {
            
        return yylineno;
    }
     
    /** Get the input stream.
     *
     */
    FILE *yyget_in  (void)
    {
            return yyin;
    }
     
    /** Get the output stream.
     *
     */
    FILE *yyget_out  (void)
    {
            return yyout;
    }
     
    /** Get the length of the current token.
     *
     */
    yy_size_t yyget_leng  (void)
    {
            return yyleng;
    }
     
    /** Get the current token.
     *
     */
     
    char *yyget_text  (void)
    {
            return yytext;
    }
     
    /** Set the current line number.
     * @param line_number
     *
     */
    void yyset_lineno (int  line_number )
    {
        
        yylineno = line_number;
    }
     
    /** Set the input stream. This does not discard the current
     * input buffer.
     * @param in_str A readable stream.
     *
     * @see yy_switch_to_buffer
     */
    void yyset_in (FILE *  in_str )
    {
            yyin = in_str ;
    }
     
    void yyset_out (FILE *  out_str )
    {
            yyout = out_str ;
    }
     
    int yyget_debug  (void)
    {
            return yy_flex_debug;
    }
     
    void yyset_debug (int  bdebug )
    {
            yy_flex_debug = bdebug ;
    }
     
    static int yy_init_globals (void)
    {
            /* Initialization is the same as for the non-reentrant scanner.
         * This function is called from yylex_destroy(), so don't allocate here.
         */
     
        (yy_buffer_stack) = 0;
        (yy_buffer_stack_top) = 0;
        (yy_buffer_stack_max) = 0;
        (yy_c_buf_p) = (char *) 0;
        (yy_init) = 0;
        (yy_start) = 0;
     
    /* Defined in main.c */
    #ifdef YY_STDINIT
        yyin = stdin;
        yyout = stdout;
    #else
        yyin = (FILE *) 0;
        yyout = (FILE *) 0;
    #endif
     
        /* For future reference: Set errno on error, since we are called by
         * yylex_init()
         */
        return 0;
    }
     
    /* yylex_destroy is for both reentrant and non-reentrant scanners. */
    int yylex_destroy  (void)
    {
        
        /* Pop the buffer stack, destroying each element. */
        while(YY_CURRENT_BUFFER){
            yy_delete_buffer(YY_CURRENT_BUFFER  );
            YY_CURRENT_BUFFER_LVALUE = NULL;
            yypop_buffer_state();
        }
     
        /* Destroy the stack itself. */
        yyfree((yy_buffer_stack) );
        (yy_buffer_stack) = NULL;
     
        /* Reset the globals. This is important in a non-reentrant scanner so the next time
         * yylex() is called, initialization will occur. */
        yy_init_globals( );
     
        return 0;
    }
     
    /*
     * Internal utility routines.
     */
     
    #ifndef yytext_ptr
    static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
    {
        register int i;
        for ( i = 0; i < n; ++i )
            s1[i] = s2[i];
    }
    #endif
     
    #ifdef YY_NEED_STRLEN
    static int yy_flex_strlen (yyconst char * s )
    {
        register int n;
        for ( n = 0; s[n]; ++n )
            ;
     
        return n;
    }
    #endif
     
    void *yyalloc (yy_size_t  size )
    {
        return (void *) malloc( size );
    }
     
    void *yyrealloc  (void * ptr, yy_size_t  size )
    {
        /* The cast to (char *) in the following accommodates both
         * implementations that use char* generic pointers, and those
         * that use void* generic pointers.  It works with the latter
         * because both ANSI C and C++ allow castless assignment from
         * any pointer type to void*, and deal with argument conversions
         * as though doing an assignment.
         */
        return (void *) realloc( (char *) ptr, size );
    }
     
    void yyfree (void * ptr )
    {
        free( (char *) ptr );   /* see yyrealloc() for (char *) cast */
    }
     
    #define YYTABLES_NAME "yytables"
     
    #line 8 "example.l"



3) Получили исходный код лексического анализатора на Си, поищем там goto

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    cat lex.yy.c |grep "goto"|wc -l
    6

Шесть строчек с goto :rolleyes:

Автор: Qraizer 14.04.15, 18:07
Так это и не паттерн. Более-менее за паттерн можно принять твой первый КА, он там goto-ами переключает состояния. А тут goto встречается в реализации. Насколько оправдано, не буду судить.

Автор: KILLER 14.04.15, 21:22
Цитата JoeUser @
Так всех кодеров на ASM'е поколечишь :lol:

Причем тут "кодеры на ASM'е" ? :huh:

Цитата JoeUser @
3) Получили исходный код лексического анализатора на Си, поищем там goto

Во первых - это нечитабельный код, сгенеренный машиной. Ты судя по всему всегда так программы и пишешь, где черт ногу сломит? Соревнуешься с компьютером - кто кого переплюнет, в плане нечитабельности?

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

Добавлено
Хотя по говнокоду - не далеко ушел.

Автор: JoeUser 14.04.15, 22:08
Цитата KILLER @
Хотя по говнокоду - не далеко ушел.

Если посчитать вхождение слов "говно" в твоих и моих постах ... в плане "говнописания" ты в явных фаворитах, не говоря уж о твоем быдлокоде :lool:
Упырь мел, человече! Когда аргументации ноль - часто на личности переходят. Почитай на досуге (п.2.4). Не убедил короче. Разговор закончен.

Добавлено
Цитата Qraizer @
Так это и не паттерн.

Ну так я и написал - частный случай реализации автомата. И Flex, и Bison - не гнушаются генерить с goto. А почему не без goto? Вопрос риторический.

Автор: KILLER 14.04.15, 22:19
Цитата JoeUser @
Если посчитать вхождение слов "говно" в твоих и моих постах ... в плане "говнописания" ты в явных фаворитах, не говоря уж о твоем быдлокоде :lool:
Упырь мел, человече! Когда аргументации ноль - часто на личности переходят. Почитай на досуге (п.2.4). Не убедил короче. Разговор закончен.

А что с моим "быдлокодом" не так? Не осилил нормальный код? Так это твои проблемы. Я то факт констатирую, а не пытаюсь кого то оскорбить, поэтому то что ты на правила съехал - говорит лишь о том, что тебе нечего возразить. Да и только.

Добавлено
Цитата JoeUser @
Упырь мел, человече!

Цитата JoeUser @
Почитай на досуге (п.2.4)


Добавлено
Цитата JoeUser @
И Flex, и Bison - не гнушаются генерить с goto. А почему не без goto? Вопрос риторический.

Только люди, которые мало понимают о чем спорят - могут приводить такие аргументы. В частности показывать на соседа и говорить: "а смотри, вася свою прогу написал и не гнушался с использованием goto , а почему не без goto? Вопрос риторический". Это многое говорит о твоем уровне.

Добавлено
Цитата JoeUser @
Разговор закончен.

А, ну и катись колбаской. Не очень то и хотелось его начинать с человеком, имеющим таки "аргументы". Как показала практика, с такими больше нервов изматаешь, нежели пояснишь им в чем их косяк, да и полчерпнуть нового с таких как ты - врятли что то можно. Аривидерчи амигос ;)

Автор: korvin 15.04.15, 15:37
Цитата JoeUser @
Как-то, так:

...

Но все равно - это не тепичный алгоритм для GOTO, нет возвратов в ветви предыдущих вычислений. Много времени прошло с момента написания этого "робота", помницца и не все реализовано было (как-то, зависание на время формирования ответа серверной частью, возвраты от пустого ответа, на повторный прием ответа и пр.). Тем не менее - в том что я написал, ИМХО, "чтение" кода и сопоставление с блок-схемой проще, нежели в твоих разбиениях на области в виде функций. Без паллитры и карандаша с бумагой не разбересся. У меня "что видно, то и написано".

Это в твоих переходах без поллитры не разберёшься. =) Qraizer правильно сказал про КА.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type StateFn func() StateFn
     
    func Start() StateFn {
        if !flagPhase {
            return Exec
        }
        if flagNeedSend {
            SendRequest()
            return nil
        }
        if flagWait {
            return nil
        }
        if flagNeedExec {
            return Exec
        }
        if flagNecessity {
            CreateRequest()
            CreateWaitingFlag()
            return nil
        }
        if flagWaitReply {
            AcceptReply()
            if !accepted {
                return nil
            }
            if reply {
                return SO
            }
            return ExecFlag
        }
        if !recieveInfExists || recieveInfOverdue {
            CreateNecessityFlag()
            return Exec
        }
        if recieveInfOverdueFinish {
            return SO
        }
        Accept()
        if updates {
            return ExecFlag
        }
        return nil
    }
     
    func SO() StateFn {
        SendSORequest()
        CreateFlag()
        return nil
    }
     
    func ExecFlag() StateFn {
        // Create exec flag
        return Exec
    }
     
    func Exec() StateFn {
        if !execPhase {
            return nil
        }
        if flagBuilding {
            CreateRequest()
            return Start
        }
        if flagExec {
            Execute()
        }
        return nil
    }
     
    func main() {
        for f := Start(); f != nil; f = f() {
        }
    }

Автор: p1qb0d 17.04.15, 08:41
С философской точки зрения, метки нужны там, где отсутствует структурное прогарммирование, например, на ассемблере, если код не выносится в функции, а пишется одной портянкой, то вызов функции заменяется goto, который вырождается в short jump или long jump. Это экономит и стек, так как никакие регистры не бэкапятся (выталкиваются) в стек и заново не перезагружаются данными. Причем, такое применение может быть как оптимизацией по скорости, так и по объему кода.

И совершенно верно замечено, что рефакторить такой код геморно.

Так что, опять же, с философской точки зрения, могу предположить, что в Си goto появилась как следствие "понижения" уровня языка до ассемблерного уровня, когда можно кодить "байт в байт", используя язык высокого уровня. Чем, например, был весьма хорош Turbo C 2.0 (и компилил тоже пушечно). И все эти техники неиспользования промежуточных переменных типа:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    {
      i = f(x);
      return i;
    }


лучше переписать как:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    {
      return f(x);
    }


и еще масса всяческих олдскульных штучек...

Так как в первом случае значение функции, которое во времена i8086 обычно вычислялось только в AX(AH,AL), сначала пересылается куда-то в память, а потом возвращается обратно в AX (где по факту уже присутствует), и только потом вызывается RET...

А сейчас и регистры стали внутри проца равноправнее, и винты толще, так что оптимизация by size тоже уже на грани удаления из компилятора напрочь...

Сейчас никто (то есть, я в частности) asm не рефакторит, поэтому и смысла в goto в высокоуровневых языках уже нет. Но его отсутствие нарушит стандарт языка. А несовместимость со стандартом гораздо страшнее. :)

Поэтому goto по-прежнему будет присутствовать, и его по-прежнему мало кто будет использовать, и это будет тоже очень правильно. И оптимизация by size тоже будет живее всех живых.

Автор: Славян 17.04.15, 13:51
Цитата p1qb0d @
Так что, опять же, с философской точки зрения, могу предположить, что в Си goto появилась как следствие "понижения" уровня языка до ассемблерного уровня, когда можно кодить "байт в байт", используя язык высокого уровня.
А я (см. первое сообщение темы) бы сказал, что вот бывает, что надо смотаться из двойного или тройного цикла, и тогда приходится городить лишний огород из переменных и т.п., либо просто сделать goto на выход и всё. Так что зависимость от ассемблера сомнительна. :scratch:

Автор: KILLER 17.04.15, 14:08
Цитата Славян @
А я (см. первое сообщение темы) бы сказал, что вот бывает, что надо смотаться из двойного или тройного цикла, и тогда приходится городить лишний огород из переменных и т.п., либо просто сделать goto на выход и всё. Так что зависимость от ассемблера сомнительна. :scratch:

Это называется костыль и лень. Когда лень писать хороший качественный код, иногда люди начинаются писать говнокод. Вот как раз это тот описываемый тобой случай - говнокода. Что мешает тебе не городить 3 вложеных цикла? А обойтись дополнительной функцией, которая сделает твой код более читабельным, понятным и сопровождаемым?

Добавлено
Кстати а еще можно все писать в одной функции main, а нахрена нам другие функции? есть же волшебный goto, а блоки можно отделять коментариями, например както так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    /*******************************************************************************/
    ...
    /*******************************************************************************/
    ...
    /*******************************************************************************/

Автор: OpenGL 17.04.15, 14:37
Цитата KILLER @
Что мешает тебе не городить 3 вложеных цикла? А обойтись дополнительной функцией, которая сделает твой код более читабельным, понятным и сопровождаемым?

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

Автор: Славян 17.04.15, 14:44
Цитата OpenGL @
Это религия какая-то. Запросто может быть так, что три цикла гораздо читабельней, чем огород с вызовом функций.
:yes:
Цитата KILLER @
Что мешает тебе не городить 3 вложеных цикла?
Циклы бывают большие и сложные. Работают со многими данными. Пересылать весь их огород в функцию - и есть костыль, непонятно для чего. ;)

Автор: Qraizer 17.04.15, 15:28
Цитата Славян @
... вот бывает, что надо смотаться из двойного или тройного цикла, и тогда приходится городить лишний огород из переменных и т.п., ...
Ладно выйти, а если войти?

Автор: Славян 17.04.15, 15:35
Цитата Qraizer @
Ладно выйти, а если войти?
Мне ни разу такого не попадалось. Но так, сходу, и проблем со входом при наличии goto не вижу. А без него - точно такой же гемор с излишними плясками с переменными да условиями. :yes-sad:

Автор: KILLER 17.04.15, 15:35
Цитата OpenGL @

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

Это не религия, 2 вложеных цикла - еще терпимо и нормально, а три и более - запутывают читающего код, а если там еще и goto, то и вовсе сбивает с толку. Если тебе так нужно запилить три цикла вложеных с возможностью выхода из третьего, оформи их в отдельную функцию/метод и ливай по return из них. Какие проблемы? Я видел функции по 5 тысяч строк кода - это читать не возможно в принципе, там и по три вложеных цикла было и по четыре. Как по мне, лучше сделать несколько мелких функций, чтобы было понятно и прозрачно, чем шаманить трехэтажные циклы.

Добавлено
Цитата Славян @
Циклы бывают большие и сложные. Работают со многими данными. Пересылать весь их огород в функцию - и есть костыль, непонятно для чего. ;)

Приведи пример такого костыля плз, я хочу посмотреть.

Добавлено
Цитата Qraizer @
Ладно выйти, а если войти?

Зачем?

Добавлено
Вообще писать можно как угодно и что угодно. Ну вот возьмет чел, напишет свою прогу в 100 000 строк тупо в функции Main, и она будет работать. Что он неверно сделал? Да все он верно сделал. Какое вам дело до того как он пишет? Религия чтоль не позволяет 100 000 строк в функции main запилить? Вот так вот и выглядят ваши аргументы со стороны.
А на деле, эта религия мне помогает избежать кучи ошибок, и когда я открываю свой код через год - то я сходу понимаю что там написано. В отличии, если бы там были трехэтажные циклы и метки - такой код открываешь через месяц и начинаешь репу чесать и заного изучать что же я там имел ввиду - делая такие нетривиальные телодвижения.

Добавлено
Плюс ко всему, есть мнение, что чем больше у тебя будет осознанных слов в проге - тем легче она будет восприниматься и читаться. В случае с трехэтажными циклами - их меньше, и можно легко запутаться, никто не будет называть счетчик больше 5-6 букв, особенно если он часто используется в теле цикла. С функциями эта проблема решаема, т.к. мало кто называет функции в 1-2 буквы. И программа становится более читабельной.

Автор: Славян 17.04.15, 15:48
Цитата KILLER @
Приведи пример такого костыля плз, я хочу посмотреть.
Пример с тремя сложными циклами со многими переменными, переписанный на "вызов функции с табуном аргументов"=костыль?.. :blink:

Автор: KILLER 17.04.15, 15:53
Цитата Славян @
Пример с тремя сложными циклами со многими переменными, переписанный на "вызов функции с табуном аргументов"=костыль?.. :blink:

Ну по твоему видимо да:
Цитата Славян @
Пересылать весь их огород в функцию - и есть костыль

Ок, приведи пример некостыля
Цитата Славян @
с тремя сложными циклами со многими переменными

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

Автор: Славян 17.04.15, 16:10
Цитата KILLER @
Ок, приведи пример некостыля...
Я просто хочу посмотреть, если я туда вставлю вызов функции - действительно хуже станет или нет.
Допустим так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int
    GetBestChessPos( void )
    {
       int count1[20];
       float terVer[8][8], pokaz;
       char superVertical[8];
       ...
       // заполняем все массивы для работы
       // ...
     
       // ниже = расчёт
       for( int row=0; row<8; row++)
          for( int col=colA; col<colH; col++)
          {
             pokaz += ...;
             if( ... ) terVer[2][5] *= ...;
             // испытываем воздействие
             if( count1[15] + (int)pow(terVer[3][4] + terVer[2][5], pokaz) > count1[2] ) // опасное положение!
               if( superVertical[2]<3 ) // мат нам грядёт
                 goto LBL_matPrideHanaBude;
          }
        ...
      LBL_matPrideHanaBude:
       // попытка рискнуть:
       ...
    }

Автор: OpenGL 17.04.15, 16:11
Цитата KILLER @
Если тебе так нужно запилить три цикла вложеных с возможностью выхода из третьего, оформи их в отдельную функцию/метод и ливай по return из них.

Это если из трёх сразу. А если из двух? Вытаскивать эти два цикла в функцию не всегда целесообразно.

Автор: KILLER 17.04.15, 16:15
Цитата OpenGL @
Это если из трёх сразу. А если из двух? Вытаскивать эти два цикла в функцию не всегда целесообразно.

Заведи флаг выхода. В чем проблема?

Цитата Славян @
Допустим так:

Что тебе мешает эти два цикла обернуть в отдельную функцию?

Автор: Славян 17.04.15, 16:25
Цитата KILLER @
Что тебе мешает эти два цикла обернуть в отдельную функцию?
Мешает то, что эти два цикла и так по сути оформлены в функцию. Плюс вагон переменных, кои засылать в функцию равносильно заново написать примерно такую же. :unsure:

Автор: KILLER 17.04.15, 16:29
Цитата Славян @
Мешает то, что эти два цикла и так по сути оформлены в функцию. Плюс вагон переменных, кои засылать в функцию равносильно заново написать примерно такую же. :unsure:

Значит стоит пересмотреть архитектуру проекта, и переписать правильно.

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

Добавлено
Можно даже вынести вот этот участок кода в отдельную функцию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
     
             if( ... ) terVer[2][5] *= ...;
             // испытываем воздействие
             if( count1[15] + (int)pow(terVer[3][4] + terVer[2][5], pokaz) > count1[2] ) // опасное положение!
               if( superVertical[2]<3 ) // мат нам грядёт
                 goto LBL_matPrideHanaBude;//тут будет return какоето значение.

Передавая в нее row и col, а уже внутри нее делать конкретные сравнения что там с чемто. Если у тебя очень много переменных в алгоритме используется, возможно стоит сгруппировать их в структуру или класс.

Автор: Славян 17.04.15, 16:34
Цитата KILLER @
значит функцию можно разгрузить - вынеся эти циклы в отдельную функцию.
Ну так и получится костыль вида:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int
    SomeStrangeFunction(...)
    {
       ...
       return p;
    }
    ...
Но замечание OpenGL'я мне показалось более полезным в возражении вам.

Добавлено
Цитата KILLER @
Если у тебя очень много переменных в алгоритме используется, возможно стоит сгруппировать их в структуру или класс.
Ну так это ж тоже костыль: для одной функции городить особый класс.
Цитата KILLER @
Передавая в нее row и col, а уже внутри нее делать конкретные сравнения что там с чемто.
Ну так мы, фактически, и будем заниматься написанием такой же функции, из которой выносим. Короче, костыль на костыле. :yes-sad:

Автор: KILLER 17.04.15, 16:50
Цитата Славян @
Ну так это ж тоже костыль: для одной функции городить особый класс.

:scratch: Почему костыль? Кто тебе такое сказал?

Цитата Славян @
Ну так и получится костыль вида:

Почему это костыль?

Цитата Славян @
Ну так мы, фактически, и будем заниматься написанием такой же функции, из которой выносим. Короче, костыль на костыле. :yes-sad:

Так я же выше писал - проще все писать в функции main, по сути ты занимаешься написанием такой же функции, из которой выносишь, когда разбиваешь код на классы и функции. В итоге по твоей логике - классы/структуры/функции(кроме main) - по сути один большой костыль.
Так ведь?

Добавлено
Цитата Славян @
Но замечание OpenGL'я мне показалось более полезным в возражении вам.

Это было не замечание. А всего лишь фантазия, призванная оправдать использование goto.

Добавлено
Ну это как знаешь пиндосы любят придумывать - "вот смотрите, у меня в руках пробирка с какой то херней, поэтому срочно нужно вводить войска в Ирак". Вот так и тут. Сам придумал проблему, сам привел ее в качестве аргумента. Неужто выход из двойного цикла такая редкая задача? Не думаю, очень часто встречается, и я , другие обходятся както без goto.

Автор: Славян 17.04.15, 17:20
Цитата KILLER @
Почему костыль? Кто тебе такое сказал?
Потому что получается, что данные надо зачем-то группировать, а группу обзывать, а это лишние обозначения, замусоривающие код. То бишь - костыль.
Цитата KILLER @
Цитата Славян @
Ну так и получится костыль вида: intSomeStrangeFunction(...)
Почему это костыль?
Как и выше: лишняя функция, лишнее название.
Цитата KILLER @
А всего лишь фантазия, призванная оправдать использование goto.
Ну я вот только что глянул исходники винды - и там вагоны goto. И ведь не сказать же, что примитивные программисты написали. Так что это не оправдание, а просто обращение внимания на самые разные случаи.

Автор: KILLER 17.04.15, 17:23
Цитата Славян @
Потому что получается, что данные надо зачем-то группировать, а группу обзывать, а это лишние обозначения, замусоривающие код. То бишь - костыль.

Действительно. Выходит 99% пишут тупой говнокод используя классы и структуры, в том числе разработчики компиляторов. В общем все 3.14дарасы а ты один дартаньян. Я понял твою логику.

Цитата Славян @
Как и выше: лишняя функция, лишнее название.

Что значит лишняя функция? О чем ты?

Цитата Славян @
Ну я вот только что глянул исходники винды - и там вагоны goto.

О еще один. Смотри вон у вовы - 100 000 строк кода в функции main, значит в этом ничего плохого нет?

Цитата Славян @
И ведь не сказать же, что примитивные программисты написали.

Это еще почему? Примитивные и пишут такой код. Возьми тот же MFC - быдло подела, быдлокодеров.

Цитата Славян @
Так что это не оправдание, а просто обращение внимания на самые разные случаи.

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

Автор: D_KEY 17.04.15, 17:23
Холивары какие-то идиотские идут... Вроде как активность какая-то, а никакого желания присоединиться не возникает.

Автор: Славян 17.04.15, 17:35
Цитата KILLER @
Действительно. Выходит 99% пишут тупой говнокод используя классы и структуры, в том числе разработчики компиляторов. В общем все <цензура> а ты один дартаньян. Я понял твою логику.
Классы (да и функции) нужно делать так, чтобы их появление было естественным, а лучше - частым, дабы оправдывать их название и вынос за main. А вы пытаетесь всё в одну кучу городить. Зачем? Верно D_KEY заметил:
Цитата D_KEY @
Холивары какие-то идиотские идут...

Автор: KILLER 17.04.15, 17:40
Цитата Славян @
Классы (да и функции) нужно делать так, чтобы их появление было естественным, а лучше - частым, дабы оправдывать их название и вынос за main.

Зачем что то выносить из main, если:
Цитата Славян @
лишняя функция, лишнее название.

Цитата Славян @
данные надо зачем-то группировать, а группу обзывать, а это лишние обозначения, замусоривающие код. То бишь - костыль.

Цитата Славян @
для одной функции городить особый класс.

Цитата Славян @
Ну так мы, фактически, и будем заниматься написанием такой же функции, из которой выносим. Короче, костыль на костыле. :yes-sad:

???

Цитата Славян @
Верно D_KEY заметил:

Он тебя и имел ввиду. Ты еще не понял? Не я же тут чушь несу :-?

Добавлено
Славян, твои "аргументы" лишены всякого смысла.

Добавлено
Вот ежели бы ты код показал - вот смотри, вот goto тут рулит, а без него - будет полный оцтой и вообще без него тебе дороже будет, чем с ним. Тогда да. Но такого аргумента не будет по определению, темболее от тебя. Потому что goto - сам по себе является костылем.

Автор: Qraizer 17.04.15, 18:48
Цитата Славян @
Цитата Qraizer @
Ладно выйти, а если войти?
Мне ни разу такого не попадалось. Но так, сходу, и проблем со входом при наличии goto не вижу. А без него - точно такой же гемор с излишними плясками с переменными да условиями. :yes-sad:
Вот-вот. Прикол в том, что выйти-то можно и без goto, нагородив вагон дополнительных условий и булевых переменных, чем на каждой итерации нагревая бедный ARM на планшете и съедая ему батарейку, а вот попробуй войди без goto. Тоже можно, доказано, но я бы руки в узел автору завязал за такой структурный фанатизм.

Автор: KILLER 17.04.15, 18:49
Цитата Qraizer @
Вот-вот. Прикол в том, что выйти-то можно и без goto, нагородив вагон дополнительных условий и булевых переменных, чем на каждой итерации нагревая бедный ARM на планшете и съедая ему батарейку, а вот попробуй войди без goto. Тоже можно, доказано, но я бы руки в узел автору завязал за такой структурный фанатизм.

Так зачем входить во вложенный цикл минуя предыдущие?

Добавлено
Цитата Qraizer @
Прикол в том, что выйти-то можно и без goto, нагородив вагон дополнительных условий и булевых переменных, чем на каждой итерации нагревая бедный ARM на планшете и съедая ему батарейку

Мда, как же сложно то все... <_<

Автор: Славян 17.04.15, 19:06
Цитата KILLER @
Зачем что то выносить из main, ...
Мы выносим f(x), если её описание длинно, а пользоваться ею придётся не раз, скажем. Аналогично с классами (POINT и т.п.). А вот выносить из функции функцию лишь бы не было goto - однократное занятие, потому и искусственное, а значит - неправильное. ;)

Автор: p1qb0d 17.04.15, 19:29
Цитата KILLER @
Это не религия, 2 вложеных цикла - еще терпимо и нормально, а три и более - запутывают читающего код, а если там еще и goto, то и вовсе сбивает с толку. Если тебе так нужно запилить три цикла вложеных с возможностью выхода из третьего, оформи их в отдельную функцию/метод и ливай по return из них. Какие проблемы? Я видел функции по 5 тысяч строк кода - это читать не возможно в принципе, там и по три вложеных цикла было и по четыре. Как по мне, лучше сделать несколько мелких функций, чтобы было понятно и прозрачно, чем шаманить трехэтажные циклы.


Это свойство ограниченности человеческого разума. Внимательно можно прочесть только половину страницы А4, если текст длиннее, такую служебную записку "не осознают".

И это опять точка зрения рефакторинга, когда код имеет свою собственную ценность сам по себе. Это не всегда верно.

Но в целом я согласен. Если блок кода {} виден на экране (стандартный текстовый режим в 25 строк), то рефакторится блестяще, два экрана - хорошо, три - нормально, четыре - терпимо, пять... Уже временные затраты на осознание того где именно и что нужно изменить перевешивают время на структурирование и переразбивку на функции. Но это опять же точка зрения рефакторинга.

Цитата KILLER @

Вообще писать можно как угодно и что угодно. Ну вот возьмет чел, напишет свою прогу в 100 000 строк тупо в функции Main, и она будет работать. Что он неверно сделал? Да все он верно сделал. Какое вам дело до того как он пишет? Религия чтоль не позволяет 100 000 строк в функции main запилить? Вот так вот и выглядят ваши аргументы со стороны.
А на деле, эта религия мне помогает избежать кучи ошибок, и когда я открываю свой код через год - то я сходу понимаю что там написано. В отличии, если бы там были трехэтажные циклы и метки - такой код открываешь через месяц и начинаешь репу чесать и заного изучать что же я там имел ввиду - делая такие нетривиальные телодвижения.

Плюс ко всему, есть мнение, что чем больше у тебя будет осознанных слов в проге - тем легче она будет восприниматься и читаться. В случае с трехэтажными циклами - их меньше, и можно легко запутаться, никто не будет называть счетчик больше 5-6 букв, особенно если он часто используется в теле цикла. С функциями эта проблема решаема, т.к. мало кто называет функции в 1-2 буквы. И программа становится более читабельной.


Это опять же точка зрения рефакторинга, то есть, повторного использования кода. Я писал проги в 200000 строк за 10 минут с использованием CASE технологий (двухкилобайтный скриптик на PL/SQL создавал многомегабайтный абсолютно безупречный и безошибочный python код, не требующий вообще никакого рефакторинга, поиска багов и тем более каких-либо собственных модификаций, в стиле GW-BASIC - сплошная функция main). Правда, по ходу выяснились ограничения python 2.7.x под Win7 - он не мог осилить такой файл. :) Это было смешно. Пришлось нарезать кусками строк по 40000, тогда скушал.

И тут совершенно непринципиально как называются переменные. Потому что вообще не требуется рефакторинг. Одноразовый код.

Так что все условно. И в конкретном частном случае все может быть совсем по-другому...

Автор: KILLER 17.04.15, 19:30
Цитата Славян @
Мы выносим f(x), если её описание длинно, а пользоваться ею придётся не раз, скажем.

Ну а если у тебя функция длинная предлинная, а выносишь ты то, что будет использованно ровно 1 раз, то и выносить незачем?

Цитата Славян @
А вот выносить из функции функцию лишь бы не было goto - однократное занятие, потому и искусственное, а значит - неправильное. ;)

Конечно не правильное, если ты будешь выносить из функции функцию лишь бы не было goto. Правильно не использовать goto, а выносить код в отдельную функцию для того - чтобы повысить читабельность кода. Я когда пишу, я не думаю как бы мне вот тут избавится от goto, поэтому по таким причинам код в отдельную функцию никогда и не выношу. А вот вынести код в отдельную функцию чтобы повысить читабельность - не проблема. Пусть даже эта функция в одном месте будет заюзана. Глядишь со временем и в другом месте пригодится.

Добавлено
Цитата p1qb0d @
Это опять же точка зрения рефакторинга, то есть, повторного использования кода. Я писал проги в 200000 строк за 10 минут с использованием CASE технологий (двухкилобайтный скриптик на PL/SQL создавал многомегабайтный абсолютно безупречный и безошибочный python код, не требующий вообще никакого рефакторинга, поиска багов и тем более каких-либо собственных модификаций, в стиле GW-BASIC - сплошная функция main). Правда, по ходу выяснились ограничения python 2.7.x под Win7 - он не мог осилить такой файл. :) Это было смешно. Пришлось нарезать кусками строк по 40000, тогда скушал.

Не ври, ты написал двухкилобайтный скриптик который что то там создавал, а не прогу в 200000 строк за 10 минут. Я тоже могу сказать что я писал 100500 строк кода за 30 секунд, тупо создал MFC проект - и у меня все сгенерилось. А чо? Не нужно путать мягкое с теплым.

Автор: Славян 17.04.15, 19:41
Цитата KILLER @
Ну а если у тебя функция длинная предлинная, а выносишь ты то, что будет использованно ровно 1 раз, то и выносить незачем?
Да, незачем. :yes:
Цитата KILLER @
Правильно не использовать goto, а выносить код в отдельную функцию для того - чтобы повысить читабельность кода.
Читабельность от разных непонятных функций не растёт, а наоборот - падает. Вот нет никакого смысла в той моей функции:
Цитата Славян @
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int
    SomeStrangeFunction(...)
    {
       ...
       return p;
    }...
А её пришлось городить, так как вы захотели от goto избавиться. :yes-sad:
Цитата KILLER @
Пусть даже эта функция в одном месте будет заюзана. Глядишь со временем и в другом месте пригодится.
Ну значит мы с вами просто по-разному смотрим на код. Бывает! ;)

Автор: KILLER 17.04.15, 19:46
Цитата Славян @
Да, незачем. :yes:

И пусть у меня функция будет на 5 тысяч строк - верно все?

Цитата Славян @
Читабельность от разных непонятных функций не растёт, а наоборот - падает.

Если они используются? Если да, то кто тебе такое сказал?

Цитата Славян @
от нет никакого смысла в той моей функции:

Конкретно вот в этой - вообще никакого смысла нет. Какие то три точки и возвращается непонятная переменная :-?

Цитата Славян @
Ну значит мы с вами просто по-разному смотрим на код. Бывает! ;)

А вы когда нибудь в своей жизни рефакторили большой код? Ну скажем там проект состоящий из 100 исходных файлов, хотя бы!!! Даже пусть будет не 100, а хотя бы 20цаточка?

Автор: p1qb0d 17.04.15, 19:50
Цитата KILLER @
Не ври, ты написал двухкилобайтный скриптик который что то там создавал, а не прогу в 200000 строк за 10 минут. Я тоже могу сказать что я писал 100500 строк кода за 30 секунд, тупо создал MFC проект - и у меня все сгенерилось. А чо? Не нужно путать мягкое с теплым.


Вынужден Вас разочаровать - Ваш проект MFC не является прогой, решающей конкретную задачу. Так что сравнение неадекватно.

Автор: Славян 17.04.15, 19:54
Цитата KILLER @
И пусть у меня функция будет на 5 тысяч строк - верно все?
Агу!
Цитата KILLER @
Если они используются? Если да, то кто тебе такое сказал?
Не "они", а "она", и не "используются", а "используется"! Но если вы про то, что много функций тоже используется, то - да, не надо выносить, ничему это не помогает. Это я вам свои ощущения пишу; соц. опрос не проводил, а по коду товарищей многократно видел, что те одноразовый блок в фунцию не выносили.
Цитата KILLER @
Конкретно вот в этой - вообще никакого смысла нет. Какие то три точки и возвращается непонятная переменная
Ясно. Вечер наступил. Ладно, прерываемся. :oops:

Автор: KILLER 17.04.15, 20:51
Цитата p1qb0d @
Вынужден Вас разочаровать - Ваш проект MFC не является прогой, решающей конкретную задачу. Так что сравнение неадекватно.

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

Цитата Славян @
Агу!

Все ясно.

Цитата Славян @
Не "они", а "она", и не "используются", а "используется"!

А вы в русском языке изучали множественное число? Или хотя бы поглядеть что я коментировал - религия не позволяет?:
Цитата Славян @
Читабельность от разных непонятных функций не растёт

Точно не "они", а "она"? Ты грузин чтоль?

Цитата Славян @
что много функций тоже используется

А я понял - функции используется! Нужно будет запомнить. Даже не так, "они(функции) используется"! :good:

Цитата Славян @
Это я вам свои ощущения пишу; соц. опрос не проводил, а по коду товарищей многократно видел, что те одноразовый блок в фунцию не выносили.

А кем ты работаешь, если не секрет?

Автор: D_KEY 17.04.15, 21:01
:facepalm:

Автор: KILLER 17.04.15, 21:05
<_<

Автор: Qraizer 17.04.15, 21:58
Цитата KILLER @
Так зачем входить во вложенный цикл минуя предыдущие?
А вот понабилось как-то. Если очень кратко – во времена DOS очень долго, как выяснилось, работающую программку захотелось по запросу прекращать с сохранением прогресса, ну и при повторном запуске восстанавливать состояние. Программка была одноразовая и сугубо для себя, я уже не помню даже, что она делала, может быть обходила все файлы по всем каталогам всех разделов и тестила студенческий лабораторный архиватор Шеннона-Фано, а может быть считала количество счастливых билетов. Но там было поболе трёх вложенных циклов со стартовыми счётчиками во внутренних, зависящими от текущих счётчиков внешних. Исходники за ненадобностью благополучно почили, но по памяти я воссоздал структурку программы вот тут. Там дальше приведён и структурный вариант... но вот найдётся ли кто-нибудь, кто скажет, что вариант без goto понятнее? <_< Не говоря уже о производительности.
И тем более о времени разработки сего шедевра дизайнерской мысли. Структурный вариант правки под "хибернет" не был написан тогда вообще, ещё в 90-ых, в течение 20 минут и нескольких итераций отладка/размышлизмы/кнопкодавство/компиляция. Тупо мозгов не хватило. Посему на структурность было забито, и на вариант с goto ушло 30 секунд, из которых 20 – удаление структурной шелухи, и его даже отлаживать не пришлось. Сейчас вот, относительно недавно, структурность была реализована за 10 минут и 3 отладочных сеанса.
Заодно могу предложить ещё одну тему для холивара: предложите наиболее удачный вариант реализовать "хибернет" в рамках вашего приложения. Сразу предупреждаю, что речь не идёт о том, чтобы переделать существующее приложение, ресь о том, чтобы "хибернет" заранее заложить в архитектуру.

Добавлено
P.S. KILLER, та ты ж должен помнить ту тему, ты же в ней участвовал.

Автор: KILLER 17.04.15, 22:33
Цитата Qraizer @
А вот понабилось как-то. Если очень кратко – во времена DOS очень долго, как выяснилось, работающую программку захотелось по запросу прекращать с сохранением прогресса, ну и при повторном запуске восстанавливать состояние. Программка была одноразовая и сугубо для себя, я уже не помню даже, что она делала, может быть обходила все файлы по всем каталогам всех разделов и тестила студенческий лабораторный архиватор Шеннона-Фано, а может быть считала количество счастливых билетов. Но там было поболе трёх вложенных циклов со стартовыми счётчиками во внутренних, зависящими от текущих счётчиков внешних. Исходники за ненадобностью благополучно почили, но по памяти я воссоздал структурку программы вот тут. Там дальше приведён и структурный вариант... но вот найдётся ли кто-нибудь, кто скажет, что вариант без goto понятнее? <_< Не говоря уже о производительности.

Во первых - это сугубо исключительные ситуации, во вторых - проще переписать как правильно, нежели как в той теме примерять костыли.

Цитата Qraizer @
P.S. KILLER, та ты ж должен помнить ту тему, ты же в ней участвовал.

Почитал - да, судя по всему учавствовал в ней. Но не помню хоть убей :-? Всетаки 5 лет назад было дело... :scratch:

Добавлено
Цитата Qraizer @
Структурный вариант правки под "хибернет" не был написан тогда вообще, ещё в 90-ых, в течение 20 минут и нескольких итераций отладка/размышлизмы/кнопкодавство/компиляция. Тупо мозгов не хватило. Посему на структурность было забито, и на вариант с goto ушло 30 секунд, из которых 20 – удаление структурной шелухи, и его даже отлаживать не пришлось.

Когда сроки жмут, а задачу нужно сделать, и ничего в голову другого не приходит - это прастительно. Но это всеравно будет костылем. Это сугубо исключительные случаи, и комент там писать обязательно нужно начинающийся с //! TODO

Добавлено
Цитата Qraizer @
Заодно могу предложить ещё одну тему для холивара: предложите наиболее удачный вариант реализовать "хибернет" в рамках вашего приложения. Сразу предупреждаю, что речь не идёт о том, чтобы переделать существующее приложение, ресь о том, чтобы "хибернет" заранее заложить в архитектуру.

Что за хибернет? У меня это слово асоциируется с джавовской приблудой, с помощью которой можно с БД работать, генерируя код из xml файлов... :scratch:

Автор: Qraizer 18.04.15, 02:59
Вот это вот самое. Hibernate - "спящий режим". Некоторые программульки имеют такую фичу. Я встречал во всякоразных паролеломалках. Можно стукнуть в "Сохранить" и ребутнуть машину, чтобы, например, винда апдейты применила, а потом включить и "Продолжить" себе на здоровье. Почему-то не вижу подобной фичи в видеоконверторах, которые тоже зачастую как начнут тромбить FRAPSовое часовое HD-сграбленное в H264, так на 4-ёх ядрах с DXVA на GTX660 можно часа на 4 получить 100% напряг CPU и GPU.
Я вот вдоволь насмотрелся на спидометры и графики в Мониторе Ресурсов за три месяца, в течении которых пополнял свой канал DOOM-ом. И ладно б хоть можно было бы оставить тромбиться и пойти спать, так нифига: есть кнопка "по окончании выключить компьютер", а про "усыпить по окончании" ни слова. И на кой хрен мне машину тушить, спрашивается, если она месяцами не ребутается, ибо три-четыре учётки постоянно залогинены, а комп совместно с WinAMPом работает будильником?

Автор: OpenGL 18.04.15, 05:41
Цитата KILLER @
Заведи флаг выхода. В чем проблема?

И это всегда будет более читабельней, чем простой goto? Сильно сомневаюсь.

Добавлено
Цитата Славян @
Мы выносим f(x), если её описание длинно, а пользоваться ею придётся не раз, скажем. Аналогично с классами (POINT и т.п.). А вот выносить из функции функцию лишь бы не было goto - однократное занятие, потому и искусственное, а значит - неправильное.

++

Автор: KILLER 18.04.15, 07:34
Цитата OpenGL @
Цитата KILLER @
Заведи флаг выхода. В чем проблема?

И это всегда будет более читабельней, чем простой goto? Сильно сомневаюсь.

Добавлено
Цитата Славян @
Мы выносим f(x), если её описание длинно, а пользоваться ею придётся не раз, скажем. Аналогично с классами (POINT и т.п.). А вот выносить из функции функцию лишь бы не было goto - однократное занятие, потому и искусственное, а значит - неправильное.

++

Да - это будет всегда лучше читабельней, чем goto. Я же нкписал - дроби на функции, если теняется читабещьноать с флагом. Факт в том - что менее читабельней будет с goto, чем без него. А ты пытаешься прицепиться конкретно к флагу. И ставишь вопрос так, что кроме флага - я больше ничего не предлагаю.

По поводу твоего ++, тоже поддерживаешь портянки на нескглько тыс. строк? Ведь славян именно это в т.ч. имел, там под чем ты подписался.

Добавлено
я в автобусе еду. набираю с телефона. Так чьто сорь за мой китайский. лень править.

Автор: Славян 18.04.15, 08:23
Цитата KILLER @
По поводу твоего ++, тоже поддерживаешь портянки на нескглько тыс. строк? Ведь славян именно это в т.ч. имел, там под чем ты подписался.
Попытку склонить OpenGL'я на свою сторону, перевирая да унижая код 'портянками', чую я... :popcorn:

Автор: KILLER 18.04.15, 08:46
у него своего мнения нет чтоль?

Автор: Славян 18.04.15, 08:57
Цитата KILLER @
у него своего мнения нет чтоль?
Конечно есть, к чему сей риторический вопрос? Просто заплёвывая чужой код/схему вы пытаетесь затащить человека к себе. Нечестно же. :blush:

Автор: OpenGL 18.04.15, 10:01
Цитата KILLER @
Да - это будет всегда лучше читабельней, чем goto. Я же нкписал - дроби на функции, если теняется читабещьноать с флагом.

Если внутренний цикл использует 100500 локальных переменных - читабельность упадёт до нуля.
И, таки я полагаю, что пример Qraizer-а ты уже переписал без goto и код стал понятнее? :D
Цитата KILLER @
По поводу твоего ++, тоже поддерживаешь портянки на нескглько тыс. строк?

Нет. Я согласен ровно с тем, что было процитировано. Портянка в тысячу строк, которую я в большинстве случаев разобью на части, из него не следует.

Автор: JoeUser 18.04.15, 11:12
Цитата KILLER @
Я же нкписал - дроби на функции, если теняется читабещьноать с флагом.

Ржу в голос! :lool: Давайте прикинем сценарий-последствие это "совета":

"Плохая программа"

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void PreCompile() {}
    void Compile() {}
    void PostCompile() {}
     
    int main()
      PreCompile();
      Compile();
      PostCompile()
      return 0;
    }


... допустим в каждой функции есть выходы из 2-3-4-вложженых циклов. В результате совета мы должны получить:

"Отличная программа"

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void PrePreCompile_1() {}
    void PrePreCompile_2() {}
    void PrePreCompile_3() {}
     
    void PreCompile() {
      PrePreCompile_1();
      PrePreCompile_2();
      PrePreCompile_3();
    }
     
    void Compile_1() {}
    void Compile_2() {}
    void Compile_3() {}
     
    void Compile() {
      Compile_1();
      Compile_2();
      Compile_3();
    }
    void PostCompile_1() {}
    void PostCompile_2() {}
     
    void PostCompile() {
      PostCompile_1()
      PostCompile_1()
    }
     
    int main()
      PreCompile();
      Compile();
      PostCompile()
      return 0;
    }


:thanks: Это ж надо еще литературным жанром обладать, чтобы дать осмысленные названия искусственно раздробленным программным сущностям. Одно радует - GOTO НЕ ПРОЙДЕТ!!! :crazy:

Автор: KILLER 18.04.15, 13:49
Цитата OpenGL @
Если внутренний цикл использует 100500 локальных переменных - читабельность упадёт до нуля.

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

Цитата OpenGL @
И, таки я полагаю, что пример Qraizer-а ты уже переписал без goto и код стал понятнее?

Запрыгиванием во вложенный for ? Во первых он привел ссылку на тему, я там в ней уже отвечал, почитай чтоль. Во вторых мне не хочеца соревноваться кто круче костыль запилит.

Цитата OpenGL @
Нет. Я согласен ровно с тем, что было процитировано. Портянка в тысячу строк, которую я в большинстве случаев разобью на части, из него не следует.

Ну представь что у тебя код, который ты разделишь на функции - будет использоваться ровно один раз, выходит ты противоречишь сам себе. Ведь какой смсл выносить код в отдельную функцию, если он используется ровно один раз(ну по вашей же логике)?

Цитата JoeUser @
Ржу в голос! Давайте прикинем сценарий-последствие это "совета":

Ну круто, сам написал говнокод какойто сам с него поржал, и еще обвинил меня в том, что это я такой говнокод предлагаю :facepalm:

Добавлено
Просто в моем понимании - функции/методы нужны для группировки некоторых инструкций по их смыслу. А в вашем понимании - вы все будете писать в одной функции в 5 тыс строк, потому что код в ней не повторяется и разделив его на функции каждая функция будет использована ровно один раз. Ну если для вас норма писать портянки - то флаг вам в руки. О чем тут еще спорить?

Автор: OpenGL 18.04.15, 14:55
Цитата KILLER @
Тогда она в любом случае упадет до нуля, хоть с goto, хоть без него. И нужно пересматривать архитектуру программы.

Голословное утверждение.
Цитата KILLER @
Запрыгиванием во вложенный for ? Во первых он привел ссылку на тему, я там в ней уже отвечал, почитай чтоль.

В той теме ответов, показывающих ненужность goto в данном случае от тебя не было.
Цитата KILLER @
Во вторых мне не хочеца соревноваться кто круче костыль запилит.

"Не хочеца" тебе в первую очередь свои слова доказывать. Ну или признать, что не получится это сделать.
Цитата KILLER @
Ну представь что у тебя код, который ты разделишь на функции - будет использоваться ровно один раз, выходит ты противоречишь сам себе. Ведь какой смсл выносить код в отдельную функцию, если он используется ровно один раз(ну по вашей же логике)?

Если ты внимательно перечитаешь ту фразу, то увидешь, что там не говорится о том, что не надо выносить в отдельную функцию если код используется один раз.
Цитата KILLER @
Просто в моем понимании - функции/методы нужны для группировки некоторых инструкций по их смыслу.

В моём тоже. И если этот самый смысл не позволяет отделить два-три цикла, из которых надо прыгать, в отдельную функцию (что, надо признать, бывает нечасто), то городить огород только ради религиозного требования "без goto" я смысла не вижу.

Автор: KILLER 18.04.15, 15:09
Цитата OpenGL @
Голословное утверждение.

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

Цитата OpenGL @
В той теме ответов, показывающих ненужность goto в данном случае от тебя не было.

Советую прочитать тему доконца хотя бы. Или это - Почему go to считается плохим тоном? (сообщение #2553668) не то?

Цитата OpenGL @
"Не хочеца" тебе в первую очередь свои слова доказывать. Ну или признать, что не получится это сделать.

Во первых я сейчас в гостях, и не со своего компа пишу. Во вторых - функцию проще переписать с нуля, чем пихать туда костыли, но при сохранении такого же кода.

Цитата OpenGL @
Если ты внимательно перечитаешь ту фразу, то увидешь, что там не говорится о том, что не надо выносить в отдельную функцию если код используется один раз.

А я тебе о чем говорю? Не дошло еще? Может сам перечитаешь с того момента, где я еще со Славяном общался по поводу этой цитаты? Как по другому еще объяснить тебе? Есть код на 5 тыс строк, ты оформляешь его в функцию, если ты его будешь дробить на функции например - то каждая функция будет использоваться ровно один раз. По вашей логике - дробить на функции такой код смысла нет, ведь все функции будут использованы один раз.

Цитата OpenGL @
В моём тоже.

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

Цитата OpenGL @
И если этот самый смысл не позволяет отделить два-три цикла, из которых надо прыгать, в отдельную функцию (что, надо признать, бывает нечасто), то городить огород только ради религиозного требования "без goto" я смысла не вижу.

Ты привязываешься к goto. Я же исхожу из того, что его нет в принципе, и как я буду писать - бить на функции или использовать флаги или использовать условие в цикле или вообще еще как то по другому - зависит от конкретной задачи. А на сухих примерах что вы приводите - как хочешь так и делай. Хоть бей на функции, хоть вводи флаг, хоть переписывай все с нуля и по своему.

Автор: Славян 18.04.15, 15:18
Цитата KILLER @
Есть код на 5 тыс строк, ты оформляешь его в функцию, если ты его будешь дробить на функции например - то каждая функция будет использоваться ровно один раз. По вашей логике - дробить на функции такой код смысла нет, ведь все функции будут использованы один раз.
Перекручиваете! Я тоже выношу одноразовую функцию, скажем из main'а a'la вида: InitGraphics(), или GetData(); Именно потому, что они несут в себе своё качество. А вот выносить из GetData() некую GetData1() сильно тупо будет, коли это ради вывертов от goto. :yes-sad:

Автор: OpenGL 18.04.15, 15:20
Цитата KILLER @
но ровно такое же голословное как и твое про 100500 локальных переменных и что если все это разбить на функции - читабельность упадет.

На работе постоянно с этим сталкиваюсь. Но у нас своя специфика - пишем гидродинамические симуляторы. Какой-то отдельный логически неделимый этап симуляции, включающий в себя достаточно много вычислений и написанный в виде метода, внутри которого немало локальных переменных - обычное дело.
Цитата KILLER @
Советую прочитать тему доконца хотя бы. Или это - Почему go to считается плохим тоном? (сообщение #2553668) не то?

Не то. У тебя там внутренний цикл вообще только один раз выполнится :)
Цитата KILLER @
Во первых я сейчас в гостях, и не со своего компа пишу. Во вторых - функцию проще переписать с нуля, чем пихать туда костыли, но при сохранении такого же кода.

Ну так я не прошу прямо сейчас :-? Просто говорить, что легко переписать, и при этом добавлять, что не собираешься это делать как-то некрасиво.
Цитата KILLER @
А я тебе о чем говорю? Не дошло еще? Может сам перечитаешь с того момента, где я еще со Славяном общался по поводу этой цитаты? Как по другому еще объяснить тебе? Есть код на 5 тыс строк, ты оформляешь его в функцию, если ты его будешь дробить на функции например - то каждая функция будет использоваться ровно один раз. По вашей логике - дробить на функции такой код смысла нет, ведь все функции будут использованы один раз.

:facepalm: Ещё раз. Использование кода в одном месте не является поводом его не-вынесения в отдельную функцию. Это фразе не противоречит.

Автор: KILLER 18.04.15, 15:22
Цитата Славян @
Перекручиваете!

Я то? Это скорее вы отнекивайтесь от своих же слов:
Цитата Славян @
Цитата
Ну а если у тебя функция длинная предлинная, а выносишь ты то, что будет использованно ровно 1 раз, то и выносить незачем?

Да, незачем.

И далее:
Цитата Славян @
Цитата
И пусть у меня функция будет на 5 тысяч строк - верно все?

Агу!


Добавлено
Цитата OpenGL @
Ещё раз. Использование кода в одном месте не является поводом его не-вынесения в отдельную функцию. Это фразе не противоречит.

Ну так еще раз прочти хотя бы предыдущую цитату мою, адресованую славяну. Я там даже уже привел по цитатам о чем шла речь.

Автор: OpenGL 18.04.15, 15:28
Цитата KILLER @
Я же исхожу из того, что его нет в принципе, и как я буду писать - бить на функции или использовать флаги или использовать условие в цикле или вообще еще как то по другому - зависит от конкретной задачи.

Так тут о том и твердят, что это религиозная заморочка, отсутствие которой иногда может помочь написать более читабельный код :)

Добавлено
Цитата KILLER @
Ну так еще раз прочти хотя бы предыдущую цитату мою, адресованую славяну.

Если я согласен с какой-либо одной фразой Славяна, то это не означает, что я буду согласен и с остальными :)

Автор: Славян 18.04.15, 15:35
Цитата KILLER @
Я то? Это скорее вы отнекивайтесь от своих же слов
Вы, KILLER, сильно нечестно поступаете! :angry:
1.У меня там были короткие предложение, а вы выдернули токмо слово, на кое и стали нападать. Фу.
2.Я не отнекиваюсь, а подтверждаю, что "выносить незачем", но если код видится особым - вполне можно. И коли функция на 5 тысяч строк, которые делают большую, сложную, но одну работу, то дробить её ради дробления - пустая работа, больше мешающая, нежели чему-то способствующая.

Автор: applegame 18.04.15, 15:40
Я не религиозен, но ни разу ни в одном языке высокого уровня не юзал goto, как-то не нужно было.

Автор: Славян 18.04.15, 15:55
Цитата applegame @
Я не религиозен, но ни разу ни в одном языке высокого уровня не юзал goto, как-то не нужно было.
Так сейчас и идёт в мире пропаганда отказа от этого слова. Даже где-то его и вовсе нет (то ли в Яве, то ли ещё где-то). Вот так вот и вырастут новые поколения людей, вынужденные писать код без такой изредка прекрасной способности! Это как исполняемые данные (DEP в винде, кажется): стараются от этого избавиться, а с ними во времена DOS/ASM'а было сверхпрекрасно!.. :'(

Автор: D_KEY 18.04.15, 16:01
Цитата applegame @
Я не религиозен, но ни разу ни в одном языке высокого уровня не юзал goto, как-то не нужно было.

Ты Си и С++ относишь к языкам высокого уровня?

Автор: Славян 18.04.15, 16:06
Цитата вики
C++ сочетает свойства как высокоуровневых, так и низкоуровневых языков.

Автор: D_KEY 18.04.15, 16:13
Я бы не стал тут опираться на вики. По этому вопросу мнения расходятся.

Автор: Славян 18.04.15, 16:26
Цитата D_KEY @
Я бы не стал тут опираться на вики. По этому вопросу мнения расходятся.
хор. :blush:

Автор: applegame 18.04.15, 16:43
Цитата D_KEY @
Ты Си и С++ относишь к языкам высокого уровня?
Ага. Не претендую на определение ЯП, но эти языки тоже имел в виду. Но таки соврал, еще в школе программируя на васике юзал goto. :)

Автор: D_KEY 18.04.15, 17:01
Ну и это забыл, видимо
Цитата applegame @
Иногда goto вполне себе хорош. Была ситуация, когда в относительно длинной функции в нескольких местах надо было прервать выполнение, сделать несколько операций и выйти из функции. Наиболее простым и понятным вариантом оказался вариант с goto:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int foo() {
      ...
      goto end;
      ...
      goto end;
      ...
      goto end;
      ...
      end:
      ...
      return;
    }
Попытки написать тоже самое структурно приводят к кучам бесполезных и плохо читаемых блоков, проверок и т.п.
Так что goto иногда можно и должно применять. Религиозные фанатики идут лесом.

Автор: Славян 18.04.15, 17:18
Цитата D_KEY @
Ну и это забыл, видимо
:good: :good: :good:

Автор: applegame 18.04.15, 18:31
Цитата D_KEY @
Ну и это забыл, видимо
Ага, забыл. Но я не религиозен. Уже не помню что там было. Кажется пилился какой-то внешний модуль для уже написанного на сях сервера ММОРПГ. И эта конструкция - бридж между модулем и сервером. Впрочем полагаю, что сейчас бы я написал по-другому.

Автор: Qraizer 18.04.15, 18:32
Кстати, D_KEY, хороший Cшный паттерн SEHового __leave. Всяко лучше, чем многоэтажные if

Добавлено
Вы как-то забыли, что критикуется не goto, как и вообще передачи управления. Критике подвергается неструктурный подход к архитектуре программы, к которой goto может легко привести. Применение же goto само по себе являться дурным тоном не может.

Автор: D_KEY 18.04.15, 18:36
В си goto нормальная конструкция для подобных случаев. В C++ разве что для выхода из вложенных циклов и пр. такого. Хотя я уже и не вспомню, когда использовал в плюсовом коде goto...

Автор: KILLER 20.04.15, 08:24
Цитата OpenGL @
Так тут о том и твердят, что это религиозная заморочка, отсутствие которой иногда может помочь написать более читабельный код :)

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

Цитата OpenGL @
Если я согласен с какой-либо одной фразой Славяна, то это не означает, что я буду согласен и с остальными :)

Так ты согласился со всем его постом, а не с конкретной фразой. Я уточнил что он под своим постом, под которым ты согласился, подразумевал. Не более.

Автор: OpenGL 20.04.15, 12:05
Цитата KILLER @
Так я и твержу, что с goto написать более читабельный код не получится, чем без него. Это костыль, когда не приходит в голову - как сделать красивее.

У Qraizer-а получилось.
Цитата KILLER @
Так ты согласился со всем его постом, а не с конкретной фразой.

В посте, с которым я согласился, не было ничего про (не)обязательность разбиения большой функции. Уточнение, в котором ты спрашивал про разбиение, я видел, но с ним я не согласен. И вообще - тебе спорить больше не о чем что ли? :D

Автор: KILLER 20.04.15, 13:41
Цитата OpenGL @
У Qraizer-а получилось.

Тот пример выглядит ужасно. Незнаю что у него и где получилось. Но я бы так не делал.

Добавлено
Цитата OpenGL @
В посте, с которым я согласился, не было ничего про (не)обязательность разбиения большой функции. Уточнение, в котором ты спрашивал про разбиение, я видел, но с ним я не согласен. И вообще - тебе спорить больше не о чем что ли? :D

Про это речь и велась в том посте, перечитай.

Автор: Qraizer 20.04.15, 13:42
Цитата OpenGL @
У Qraizer-а получилось.
Я старался :blush: .
Вообще, у меня стойкое ощущение, что разговор идёт в том же духе, как если бы обсуждались "исключения vs коды ошибок". Исключения по своей природе несруктурны, и без них вполне можно прожить, однако многие считают и не без оснований, что зачастую с ними проще. Фактически это означает, что не всегда структурный код лучше несруктурного.
С goto такая же петрушка, вот и весь сказ. Просто goto не позволяет покинуть пределы функции. Да, он не будет вызывать деструкторы блоков, пределы которых он покидает, но в пределах функции ненужность этого ввиду отсутствия затрагиваемых локальных объектов относительно несложно ревьюжится, зато работает быстрее. Так же как отсутствие затрагиваемых локальных объектов (будучи доказанным) может служить мотивацией замены исключений на <csetjmp>.
Остальные случаи применения goto, несвязанные с обработкой ошибок, можно пересчитать по пальцам.

Автор: OpenGL 20.04.15, 14:08
Цитата KILLER @
Тот пример выглядит ужасно. Незнаю что у него и где получилось. Но я бы так не делал.

Напиши, как бы ты делал.
Цитата KILLER @
Про это речь и велась в том посте, перечитай.

:facepalm: Ну как скажешь. Объяснять семантику слов русского языка и введение в курс элементарной логики мне в общем-то неинтересно.

Автор: KILLER 20.04.15, 18:11
Цитата OpenGL @
Напиши, как бы ты делал.

Пусть Qraizer сформулирует задачу. Я напишу. Ибо тот код в принципе - отражение одного конкретного мнения, как сделал он.

Цитата OpenGL @
:facepalm: Ну как скажешь. Объяснять семантику слов русского языка и введение в курс элементарной логики мне в общем-то неинтересно.

Объясняю:
Цитата Славян @
Мы выносим f(x), если её описание длинно, а пользоваться ею придётся не раз, скажем. Аналогично с классами (POINT и т.п.). А вот выносить из функции функцию лишь бы не было goto - однократное занятие, потому и искусственное, а значит - неправильное. ;)

В этоп посте, идет речь о том, что выносится все, что описано длинно и будет заюзано не один раз в конечном итоге, так ясно?

Автор: OpenGL 20.04.15, 18:28
Цитата KILLER @
В этоп посте, идет речь о том, что выносится все, что описано длинно и будет заюзано не один раз в конечном итоге, так ясно?

Да. Хочешь сказать, что из этого следует, что всё, что этим пунктам не удовлетворяет, выноситься не должно?

Добавлено
Цитата KILLER @
Пусть Qraizer сформулирует задачу.

В той теме всё есть, в общем-то.

Автор: Qraizer 20.04.15, 20:32
Та чё там формулировать-то. Есть код без всяких излишеств. Вдруг понадобилось научить его по событию сохранять прогресс и терминатиться, а позже при запуске восстанавливать сохранённый прогресс. Всё.

P.S. Я ж предложил интересную тему для Холивара: методы заранее заложить в дизайн вот такой вот "хибернет", только уровня приложения, а не всего компа.

Автор: applegame 20.04.15, 20:58
Цитата Qraizer @
Вдруг понадобилось научить его по событию сохранять прогресс и терминатиться, а позже при запуске восстанавливать сохранённый прогресс. Всё.

P.S. Я ж предложил интересную тему для Холивара: методы заранее заложить в дизайн вот такой вот "хибернет", только уровня приложения, а не всего компа.
Файберы ака сопрограммы?

Автор: KILLER 20.04.15, 21:06
Цитата OpenGL @
Да. Хочешь сказать, что из этого следует, что всё, что этим пунктам не удовлетворяет, выноситься не должно?

Переформулируй, не понял ответа твоего, вернее могу не так ответить, так как не понял смысла того что ты написал. В общем переформулируй вопрос.

Цитата OpenGL @
В той теме всё есть, в общем-то.

Нет там ничего, есть просто код, от фонаря, который вкинул Qraizer.

Добавлено
Цитата Qraizer @
Та чё там формулировать-то. Есть код без всяких излишеств. Вдруг понадобилось научить его по событию сохранять прогресс и терминатиться, а позже при запуске восстанавливать сохранённый прогресс. Всё

Ну так по событию и терминатится, а при запуске востанавливать сохраненный прогресс? :-?

Добавлено
Вообще про эти хибернейты что ты писал Qraizer - можно отдельную тему в холиварах создавать, правда хз что там холиварить, но я точно до 20 мая ниче серьезного не буду писать в принципе, ибо нет времени в принципе. День на работе, вечер - занят под завязку. После 20 мая, ближе к лету, по идее должно появится куча времени. И если ты сформулируешь ТЗ, я очень был бы тебе благодарен, так как у меня после 20 мая будет(я надеюсь) период востановления после C#, и я очень хотел бы чтобы ты эту задачу сформулировал более яснее, я ее добавлю это в закладки, мне это нужно, ибо в тематике ноль полный.
Цитата Qraizer @
Заодно могу предложить ещё одну тему для холивара: предложите наиболее удачный вариант реализовать "хибернет" в рамках вашего приложения. Сразу предупреждаю, что речь не идёт о том, чтобы переделать существующее приложение, ресь о том, чтобы "хибернет" заранее заложить в архитектуру.

Вот например - продолжи мыслю. Например - что за программа(от фонаря например) - плеер там, БД, еще что то? Просто хотя бы немного распиши ТЗ плз. Немного нужно реабилитироваться, как раз пригодится.

Автор: Qraizer 20.04.15, 22:08
Та неважно, что. И не надо ничего писать, разве что проиллюстрировать идею, например. Как в архитектуру заложить сохранение/восстановление состояний? В конце-концов любая нормальная игра это умеет. Но вот как сие реализовать в своём приложении?

Автор: OpenGL 21.04.15, 01:49
Цитата KILLER @
Переформулируй, не понял ответа твоего, вернее могу не так ответить, так как не понял смысла того что ты написал. В общем переформулируй вопрос.

Ок, в последний раз. Во фразе говорится об одном из критериев, соответствие коду которому говорит о том, что код стоит вынести в отдельный метод. Об одном из. Это не значит, что не существуют других критериев, и, как следствие - не значит, что большой код, написанный один раз, я разбивать не буду.

Автор: D_KEY 21.04.15, 10:32
Цитата KILLER @
Цитата OpenGL @
У Qraizer-а получилось.

Тот пример выглядит ужасно.

Что за пример-то? Я пропустил, видимо.

Автор: KILLER 21.04.15, 10:35
Цитата D_KEY @
Что за пример-то? Я пропустил, видимо.

Почему go to считается плохим тоном?

Автор: D_KEY 21.04.15, 10:35
Цитата Qraizer @
Та неважно, что. И не надо ничего писать, разве что проиллюстрировать идею, например. Как в архитектуру заложить сохранение/восстановление состояний? В конце-концов любая нормальная игра это умеет. Но вот как сие реализовать в своём приложении?

Это любопытно, но при чем тут goto? :D

Добавлено
Цитата KILLER @
Цитата D_KEY @
Что за пример-то? Я пропустил, видимо.

Почему go to считается плохим тоном?

Забавно :)

Автор: _lcf_ 21.04.15, 10:41
рискну предположить, что тройной for можно заменить while с ручным инкрементом - необходимость в гоуту отпадёт :rolleyes:

Автор: D_KEY 21.04.15, 10:42
Да там вообще просто переделать инициализацию i,j,k и все. То, что с goto читабельнее - вопрос спорный ;)

Автор: _lcf_ 21.04.15, 10:47
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = j = k = 0;
    restorePrevious(&i, &j, &k);
     
    while(i<10)
    {
     while(j<10)
     {
      while(k<10)
      {
       k++;
      }
      j++; k = j;
     }
     i++; j = i; k = j;
    }

оно?

Автор: D_KEY 21.04.15, 10:53
_lcf_, тут еще вопрос с масштабированием. Решение Qraizer'а позволяет сохранять много всякой херни, загружать много всякой херни и работать дальше. Как бы в твоем случае мы будем чуть ли не с каждой переменной вручную разбираться.

Автор: _lcf_ 21.04.15, 11:09
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for (int i = isInit ? initValue : 0; i < 10; i++)
     for (int j = isInit ? initValue : i; j < 10; j++)
      for (int k = isInit ? initValue : j; k < 10; k++)

компилятор сиё оптимизирует?

Автор: D_KEY 21.04.15, 11:12
У них разные initValue :)
По поводу оптимизации - надо мерить.

Добавлено
Пока получается что-то вроде того:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = 0, j = 0, k = 0;
    if (restore(&i, &j, &k))
        goto resume;
     
    for(i = 0; i < 10; ++i) {
      for (j = i; j < 10; ++j) {
        for(k = j; k < 10; ++k) {
    resume:
         // ...
     
        }
      }
    }


vs

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = 0, j = 0, k = 0;
    restore(&i, &j, &k);
     
    for(; i < 10; ++i, j = i, k = j) {
      for (; j < 10; ++j, k = j) {
        for(; k < 10; ++k) {
     
          // ...
     
        }
      }
    }


vs

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int init_i = 0, init_j = 0, init_k = 0;
    bool is_init = restore(&init_i, &init_j, &init_k);
     
    for(int i = is_init ? init_i : 0; i < 10; ++i) {
      for (int j = is_init ? init_j : i; j < 10; ++j) {
        for(int k = is_init ? init_k : j; k < 10; ++k) {
     
          // ...
     
        }
      }
    }


Мне кажется, что вариант с goto понятнее... Хотя второй тоже неплох, на мой взгляд.

Автор: korvin 21.04.15, 11:24
Цитата D_KEY @
_lcf_, тут еще вопрос с масштабированием. Решение Qraizer'а позволяет сохранять много всякой херни, загружать много всякой херни и работать дальше. Как бы в твоем случае мы будем чуть ли не с каждой переменной вручную разбираться.

Э-м… Так там же тоже с каждой переменной вручную разбираются:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      ok = ok && inFile.read(reinterpret_cast<char*>(&i), sizeof(i)).gcount()==sizeof(i);
      ok = ok && inFile.read(reinterpret_cast<char*>(&j), sizeof(j)).gcount()==sizeof(j);
      ok = ok && inFile.read(reinterpret_cast<char*>(&k), sizeof(k)).gcount()==sizeof(k);
    // ...
         ok = ok && outFile.write(reinterpret_cast<char*>(&i), sizeof(i)).good();
         ok = ok && outFile.write(reinterpret_cast<char*>(&j), sizeof(j)).good();
         ok = ok && outFile.write(reinterpret_cast<char*>(&k), sizeof(k)).good();

Автор: D_KEY 21.04.15, 11:28
Цитата korvin @
Цитата D_KEY @
_lcf_, тут еще вопрос с масштабированием. Решение Qraizer'а позволяет сохранять много всякой херни, загружать много всякой херни и работать дальше. Как бы в твоем случае мы будем чуть ли не с каждой переменной вручную разбираться.

Э-м… Так там же тоже с каждой переменной вручную разбираются:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      ok = ok && inFile.read(reinterpret_cast<char*>(&i), sizeof(i)).gcount()==sizeof(i);
      ok = ok && inFile.read(reinterpret_cast<char*>(&j), sizeof(j)).gcount()==sizeof(j);
      ok = ok && inFile.read(reinterpret_cast<char*>(&k), sizeof(k)).gcount()==sizeof(k);
    // ...
         ok = ok && outFile.write(reinterpret_cast<char*>(&i), sizeof(i)).good();
         ok = ok && outFile.write(reinterpret_cast<char*>(&j), sizeof(j)).good();
         ok = ok && outFile.write(reinterpret_cast<char*>(&k), sizeof(k)).good();

Ну это только в плане сохранения/загрузки. А так тебе придется еще в логике учитывать. В данном случае - по особому работать со счетчиками циклов.

Автор: korvin 21.04.15, 11:29
Цитата D_KEY @
Мне кажется, что вариант с goto понятнее...

O_O'

Чем он понятней? Только тем, что там выход из трёх циклов? Ну так достаточно весь этот код вынести из main в отдельную процедуру и использовать return. В goto-версии циклы начинаются с нуля. Если вдруг при чтении файла что-то пойдёт не так и загрузятся неправильные значения счётчиков (ниже нуля), то получим баг.

Добавлено
Цитата D_KEY @
Ну это только в плане сохранения/загрузки. А так тебе придется еще в логике учитывать. В данном случае - по особому работать со счетчиками циклов.

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

Автор: D_KEY 21.04.15, 11:36
Есть еще вариант со switch :D
Не проверял, мог опечататься

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = 0, j = 0, k = 0;
    switch (restore(&i, &j, &k))
    case 0:
      for(i = 0; i < 10; ++i) {
        for (j = i; j < 10; ++j) {
          for(k = j; k < 10; ++k) {
    default:
             // ...
        
          }
        }
      }
    }


Вряд ли кто-то скажет, что он понятнее варианта с goto :)

Автор: _lcf_ 21.04.15, 11:38
а еще можно про указатели вспомнить :whistle:

Автор: D_KEY 21.04.15, 11:38
Цитата korvin @
Цитата D_KEY @
Мне кажется, что вариант с goto понятнее...

O_O'

Чем он понятней? Только тем, что там выход из трёх циклов?

Там не выход, там вход :)

Цитата
Ну так достаточно весь этот код вынести из main в отдельную процедуру и использовать return.

Покажи.

Цитата
В goto-версии циклы начинаются с нуля. Если вдруг при чтении файла что-то пойдёт не так и загрузятся неправильные значения счётчиков (ниже нуля), то получим баг.

Это задача функции restore. Допустим, она по контракту гарантирует, что все счетчики будут корректными.

Добавлено
Цитата korvin @
Чем он понятней?

Тем, что там этот переход никак не сказывается на логике.

Автор: OpenGL 21.04.15, 11:45
Цитата D_KEY @
Есть еще вариант со switch

Прикольно :D Но от goto практически не отличается.

Автор: D_KEY 21.04.15, 11:51
Цитата OpenGL @
Но от goto практически не отличается.

Да абсолютно тоже самое. Только выглядит пугающе. Зато "без goto" :D

Добавлено
Цитата OpenGL @
Цитата D_KEY @
Есть еще вариант со switch

Прикольно :D

Я думаю, ты видел машину Даффа, стейтмашины на этом деле и бесстековые сопрограммы(например, в boost/asio/coroutine.hpp).

Автор: korvin 21.04.15, 11:59
Цитата D_KEY @
Там не выход, там вход :)
Это задача функции restore. Допустим, она по контракту гарантирует, что все счетчики будут корректными.

Да, поспешил.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo()
    {
        int i, j, k;
        if !restore(&i, &j, &k) {
            i = 0;
            j = 0;
            k = 0;
        }
        for(; i < 10; i++, j = 0)
            for(; j < 10; j++, k = 0)
                for(; k < 10; k++)
                    if !dowork(i, j, k) {
                        save(i, j, k);
                        return;
                    }
    }

Автор: OpenGL 21.04.15, 12:04
Неплохо. Уже почти дошло до goto по читабельности :) Только циклы не от нуля надо начинать.

Автор: korvin 21.04.15, 12:08
Цитата OpenGL @
Неплохо. Уже почти дошло до goto по читабельности :) Только циклы не от нуля надо начинать.

Они и не от нуля начинаются. Обнуление нижестоящего счётчика происходит только после прохождения им этапа от некоего загруженного значения до максимального при изменении вышестоящего счётчика.

Автор: OpenGL 21.04.15, 12:14
Цитата korvin @
Обнуление нижестоящего счётчика происходит только после прохождения им этапа от некоего загруженного значения до максимального при изменении вышестоящего счётчика.

Я как раз о том, что там не обнуление нужно, а что-то такое:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        for(; i < 10; i++, j = i)
            for(; j < 10; j++, k = j)

Автор: korvin 21.04.15, 12:30
Цитата OpenGL @
Я как раз о том, что там не обнуление нужно, а что-то такое:

Вообще-то как раз не нужно, нужно именно обнуление. Я понимаю, что тот код с goto понять не просто, но ты постарайся. =)

Там суть в том, что мы попадаем в произвольное место «трёхмерного» цикла. Т.е. тот код создаёт последовательность
[0,0,0],[0,0,1],[0,0,2]...[0,1,0],[0,1,1],[0,1,2]...[0,2,0],[0,2,1],[0,2,2]...
И мы попадаем в произвольное место, например в [2,3,4], цикл по k один раз проходит [4..9] и при всех следующих итерациях j начинается с нуля [0..9], как обычно, а не с загруженного значения 4. Так же и цикл по j, один раз проходит [3..9], а потом постоянно [0..9].

Автор: OpenGL 21.04.15, 12:41
Почему обнуление? В том коде i <= j <= k. У тебя же это не выполняется.

Автор: korvin 21.04.15, 12:46
Цитата OpenGL @
Почему обнуление? В том коде i <= j <= k. У тебя же это не выполняется.

Да нет же. Там происходит обнуление k при каждой итерации по j и обнуление j при каждой итерации по i. Просто с помощью goto мы попадаем в некое состояние [i,j,k] = [2,3,4] например. Выполняется тело цикла по k до 9, происходит итерация j и k обнуляется.

Автор: OpenGL 21.04.15, 13:08
Цитата Qraizer @
 
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for(i=0; i<10; ++i)
      for(j=i; j<10; ++j)
       for(k=j; k<10; ++k)


Где тут обнуление?

Автор: korvin 21.04.15, 13:16
Цитата OpenGL @
Цитата Qraizer @
 
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for(i=0; i<10; ++i)
      for(j=i; j<10; ++j)
       for(k=j; k<10; ++k)


Где тут обнуление?

Что-то я сегодня вообще невнимателен (видимо из-за простуды). Показалось, что там циклы с нуля начинаются.

Автор: D_KEY 21.04.15, 13:20
Цитата OpenGL @
Цитата korvin @
Обнуление нижестоящего счётчика происходит только после прохождения им этапа от некоего загруженного значения до максимального при изменении вышестоящего счётчика.

Я как раз о том, что там не обнуление нужно, а что-то такое:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        for(; i < 10; i++, j = i)
            for(; j < 10; j++, k = j)

Чем это отличается от моего второго варианта?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        int i = 0, j = 0, k = 0;
        restore(&i, &j, &k);
        
        for(; i < 10; ++i, j = i, k = j) {
          for (; j < 10; ++j, k = j) {
            for(; k < 10; ++k) {
        
              // ...
        
            }
          }
        }

Автор: korvin 21.04.15, 13:52
Объясните, почему после 0,3,3 идёт 1,2,2?

Автор: D_KEY 21.04.15, 13:54
Цитата korvin @
Объясните, почему после 0,3,3 идёт 1,2,2?

Так лучше?

Автор: korvin 21.04.15, 13:55
Цитата D_KEY @
Чем это отличается от моего второго варианта?

Тем, что я вообще уже не соображаю. =)

Добавлено
Цитата D_KEY @
Так лучше?

Да, допёрло.

Лучше б просто сделали последовательность комбинаций, хоть это и менее эффективно, зато вообще никаких непоняток. =)

Автор: Qraizer 22.04.15, 03:47
Ого. Подтянулась тяжёлая артиллерия.

Автор: korvin 23.04.15, 12:05
Я что вспомнил: есть же ещё продолжения:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #lang racket
     
    (define i-save 0)
    (define j-save 0)
    (define k-save 0)
     
    (define (save i j k)
      (set! i-save i)
      (set! j-save j)
      (set! k-save k)
      (printf "\nsaved\n"))
     
    (define (restore)
      (printf "restored\n")
      (values i-save j-save k-save))
     
    (define N 4)
     
    (define (start #:resume? (resume? #f))
      (define-values (i j k)
        (if resume?
            (restore)
            (values 0 0 0)))  
      (let/cc abort
        (define (for-i i0)
          (for ((i (in-range i0 N)))
            (for-j i i)))
     
        (define (for-j i j0)
          (for ((j (in-range j0 N)))
            (for-k i j j)))
     
        (define (for-k i j k0)
          (for ((k (in-range k0 N)))
            (when (and (not resume?) (= i 1) (= j 2) (= k 3))
              (save i j k)
              (abort))
            (printf "[~a,~a,~a], " i j k)))
        
        (for-k i j k)
        (for-j i (+ j 1))
        (for-i (+ i 1))
        (printf "\ndone\n")))
     
     
     
    (define (main)
      (start)
      (start #:resume? #t))
     
    (main)


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    [0,0,0], [0,0,1], [0,0,2], [0,0,3], [0,1,1], [0,1,2], [0,1,3], [0,2,2], [0,2,3], [0,3,3], [1,1,1], [1,1,2], [1,1,3], [1,2,2],
    saved
    restored
    [1,2,3], [1,3,3], [2,2,2], [2,2,3], [2,3,3], [3,3,3],
    done

Автор: D_KEY 23.04.15, 15:05
Даже читать не хочется :lol:

Автор: korvin 23.04.15, 15:19
Цитата D_KEY @
Даже читать не хочется :lol:

Что так? Ты ж не боишься скобок, вроде. =)

Автор: Serafim 05.05.15, 21:47
Цитата Славян @
Немного в сторону, но подскажите, почему бы не внедрить этакий "break break"

В php пишется break 2; :tong:

Добавлено
А блин, на 16ой странице у вас вообще другая тема пошла, пойду я...

Автор: Славян 07.05.15, 16:08
Цитата Serafim @
В php пишется break 2; :tong:
Везёт вам! Но сразу возникает вопрос: что означает "break 0;" или "break -1;" ?
1.Я бы сказал, что "break 0;" означает ассемблерный NOP - ничего не делаем. :scratch:
2."break -1;" означает "вход в ближайший цикл"! :crazy: Только непонятно, ближайший спереди или сзади... :blush:

Автор: OpenGL 07.05.15, 17:14
Цитата Славян @
Но сразу возникает вопрос: что означает "break 0;" или "break -1;" ?

Очевидно, ошибку компиляции :)

Автор: Славян 07.05.15, 17:25
Цитата OpenGL @
Очевидно, ошибку компиляции :)
Да логично, конечно, но хочется, чтобы это перестало быть ошибкой и вот тут и приходится задуматься над возможной допустимой реализацией такого поведения. :whistle:

Автор: Serafim 07.05.15, 17:32
Цитата Славян @
Но сразу возникает вопрос: что означает "break 0;" или "break -1;" ?

Сейчас - ошибку (EngineException). До версии 5.4 break 0 означало тоже самое, что и break 1, т.е. выход из цикла первого уровня.

Автор: OpenGL 07.05.15, 17:36
Цитата Славян @
но хочется, чтобы это перестало быть ошибкой и вот тут и приходится задуматься над возможной допустимой реализацией такого поведения.

Какой смысл? Давай тогда вообще break 3.1415926 или break (1 - 3i) введём, чего мелочиться-то :D

Автор: Serafim 07.05.15, 17:36
Смысл удаления в том, что до той же версии (5.4) внутрь break допускалось передавать переменные (т.е. можно было указывать уровень цикла с помощью int), что позволяло творить магию, которая могла породить подобные конструкции (циферку 0 и отрицательные значения (включая даже флоты и строковые типы)), дабы избежать этих проблем и упростить жизнь - оно приводилось к единице.

Потом это поведение вырезали (т.е. сейчас можно указывать только константные значения и только int > 0), чтоб у читающих код не было проблем с психикой, когда они видят break mt_rand(0, 9999);. Как следствие убрали это неявное приведение, т.к. никто в своём уме не будет писать break 0; - это не имеет смысла.

Автор: Славян 07.05.15, 17:46
Цитата OpenGL @
Какой смысл? Давай тогда вообще break 3.1415926
Да, это будет после того, как научимся работать с целыми. :yes:
Цитата OpenGL @
или break (1 - 3i) введём, чего мелочиться-то
Не, пока код у нас линеен - плоские переходы нельзя ввести. :yes-sad: Вот будет граф кода пушистее - там=тогда и можно будет и о таком подумать... :whistle:

Автор: Qraizer 07.05.15, 17:47
А зря. Это был прекрасный код конём для взбунтовавшегося ИИ. Типа "коли ты такой умный, ну-ка, исполни break -i, родимый", и делов. Теперь вся надежда только на Сару или Нео.

Автор: Славян 07.05.15, 17:54
Цитата Qraizer @
Это был прекрасный код конём для взбунтовавшегося ИИ. Типа "коли ты такой умный, ну-ка, исполни break -i, родимый", и делов.
Да ничего прекрасного. Си'шному коду тоже следовало бы на такое ругаться:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i=3.14;
Но программисты решили за ИИ вперёд подумать.

Добавлено
Цитата Славян @
Си'шному коду тоже следовало бы на такое ругаться
О, оказывается по-лёгкому ругается:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    warning C4244: инициализация: преобразование "float" в "int", возможна потеря данных

Автор: Serafim 07.05.15, 18:11
даёшь хардкор:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = Integer.valueOf(Math.PI);

и никаких варнингов :whistle:

правда это не сишки... =(

Добавлено
Короче вангую, что первый ИИ, который поработит мир будет на пыхе или джаве :yes: т.к. они умеют всё

Автор: Славян 07.05.15, 18:16
Цитата Serafim @
Короче вангую, что первый ИИ, который поработит мир будет на пыхе или джаве
Отвратное вангование. Даю руку на отсечение, что вы заблуждаетесь. ;)

Автор: Serafim 07.05.15, 20:05
Почему же? Другие ИИ будут падать на просьбах ответить на вопрос о жизни и всём таком, жава и пых же скастуют его к инту и вернут нормальный числовой ответ без единого варнинга. Так даже в книге Дугласа было.

Автор: D_KEY 07.05.15, 22:45
Так и си кастует. А ИИ тут каким боком?

Автор: Славян 08.05.15, 01:21
Цитата Serafim @
Другие ИИ будут падать на просьбах ответить на вопрос о жизни и всём таком, жава и пых же скастуют его к инту и вернут нормальный числовой ответ без единого варнинга.
Ответов на вопрос о жизни у людей полно всяких, а потому для ИИ это не будет проблемой вообще. Чтобы ИИ мог захватить мир, ему надо как минимум научиться отвечать, что делает конструкция "break -5" в правильно расширенном PHP, а тут даже вы пока ответ не предложили! :scratch:

Автор: Qraizer 08.05.15, 02:51
Цитата Славян @
что делает конструкция "break -5" в правильно расширенном PHP
Фигня вопрос. Даже я могу ответить: идёт в пять циклов вглубь. А вот break -3i куда ведёт, а? Вбок?

Добавлено
А вот ещё вопрос на засыпку: что делает конструкция continue 3?

Автор: Славян 08.05.15, 11:03
Цитата Qraizer @
Фигня вопрос. Даже я могу ответить: идёт в пять циклов вглубь.
Да я так же предложил, только что-то не смог сразу решить вглубь следующего за строкой цикла или предыдущего?
Цитата Qraizer @
А вот break -3i куда ведёт, а? Вбок?
Да, будет вбок, когда код будет не тупо линейно устроен. Но пока такого нигде нет, а многопоточность только-только начинает в примерно этом направлении работать.
Цитата Qraizer @
А вот ещё вопрос на засыпку: что делает конструкция continue 3?
А что, в PHP есть какие-то попытки реализации "continue N"? Но, в целом, это похоже на конструкцию "break (N-1), continue;"

Автор: DarkEld3r 08.05.15, 12:03
Цитата Славян @
"continue N"? Но, в целом, это похоже на конструкцию "break (N-1), continue;"

Почему не "пропустить N итераций цикла"? :D

Автор: Славян 08.05.15, 12:12
Цитата DarkEld3r @
Почему не "пропустить N итераций цикла"?
Хм... да, возможно. Но подумалось, что "continue 0"=="continue;".

Автор: Qraizer 08.05.15, 14:04
DarkEld3r получает плюс. Вариант "продолжить итерации N-го наружу цикла" по аналогии с break N не канает, потому что так неинтересно.
Славян, ну всё правильно, 0 – значит никаких итераций не пропускать, просто перейти к следующей.

Автор: Славян 08.05.15, 14:21
Цитата Qraizer @
DarkEld3r получает плюс. Вариант "продолжить итерации N-го наружу цикла" по аналогии с break N не канает, потому что так неинтересно.
Славян, ну всё правильно, 0 – значит никаких итераций не пропускать, просто перейти к следующей.
Да, согласен с вами обоими. Для продолжения N-го наружу и послужат команды "break N-1,continue".

Добавлено
Т.е. эта схема:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for(A;B;C)
    {
      P;
      continue N;
      Q;
    }
работает так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for(A;B;C)
    {
      P;
      C;...;C; continue; // C; - N раз
      Q;
    }
? А как работает схема:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    do
    {
      P;
      continue N;
      Q;
    }while( A );
? :-?

Автор: amk 08.05.15, 17:32
Если честно, goto с меткой перехода куда нагляднее, чем все эти break 3, или continue 2.

Автор: D_KEY 08.05.15, 21:40
Цитата amk @
Если честно, goto с меткой перехода куда нагляднее, чем все эти break 3, или continue 2.

+1

Автор: Serafim 08.05.15, 22:42
Настоящие коммерческие программисты, использующие скрам - не используют goto

Добавлено
тем более любой алгоритм можно без goto реализовать, более чище и понятнее, чем с ним.

Добавлено
Например вот это приводилось в пример: goto vs break & continue (сообщение #3188868)
Это ради того, чтобы не выносить метку в отдельную функцию или на худой конец замыкание, т.е. ни разу не оправдание сего пагубного поступка, а ведь ещё г-н Макконнел говорил....

Добавлено
И наконец реализация мульти-континью на пыхе.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    foreach ($array as $item) {
      // пропускаем три шага
      foreach (range(1, 3) as $i) { next($array); }
    }


Смысл в том, что итераторы внутри имеют курсор (ля реализации таких циклов, как for..in, foreach...as, for..of и проч.), а функция next просто его сдвигает на следующий.

Добавлено
*я подозреваю, что курсоры можно двигать и не только в пыхе, как минимум во всех языках где есть итераторы

Автор: applegame 09.05.15, 06:09
Цитата Serafim @
Например вот это приводилось в пример: goto vs break & continue (сообщение #3188868)
Это ради того, чтобы не выносить метку в отдельную функцию или на худой конец замыкание, т.е. ни разу не оправдание сего пагубного поступка, а ведь ещё г-н Макконнел говорил....
В плюсах тогда не было лямбд с замыканиями, а выносить в отдельную функцию плохо, так как нет доступа к локальным переменным. Короче FUUUUUUUUUUUUUUU!

Автор: OpenGL 09.05.15, 06:35
Цитата Serafim @
тем более любой алгоритм можно без goto реализовать, более чище и понятнее, чем с ним.

В этой теме уже приводился алгоритм, который без goto менее нагляден :)

Автор: D_KEY 09.05.15, 07:31
Serafim, ты пример Qraizer'а видел? Покажи его вариант, который был бы лучше варианта с goto. Там не выход из циклов, там вход :)
Вот небольшое резюме разных вариантов:
Цитата D_KEY @
Пока получается что-то вроде того:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = 0, j = 0, k = 0;
    if (restore(&i, &j, &k))
        goto resume;
     
    for(i = 0; i < 10; ++i) {
      for (j = i; j < 10; ++j) {
        for(k = j; k < 10; ++k) {
    resume:
         // ...
     
        }
      }
    }


vs

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int i = 0, j = 0, k = 0;
    restore(&i, &j, &k);
     
    for(; i < 10; ++i, j = i, k = j) {
      for (; j < 10; ++j, k = j) {
        for(; k < 10; ++k) {
     
          // ...
     
        }
      }
    }


vs

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int init_i = 0, init_j = 0, init_k = 0;
    bool is_init = restore(&init_i, &init_j, &init_k);
     
    for(int i = is_init ? init_i : 0; i < 10; ++i) {
      for (int j = is_init ? init_j : i; j < 10; ++j) {
        for(int k = is_init ? init_k : j; k < 10; ++k) {
     
          // ...
     
        }
      }
    }


Мне кажется, что вариант с goto понятнее... Хотя второй тоже неплох, на мой взгляд.

Автор: Serafim 09.05.15, 10:06
Цитата OpenGL @
В этой теме уже приводился алгоритм, который без goto менее нагляден

акстись, тут 18 страниц

Добавлено
Цитата applegame @
В плюсах тогда не было лямбд с замыканиями, а выносить в отдельную функцию плохо, так как нет доступа к локальным переменным. Короче FUUUUUUUUUUUUUUU!

передать в качестве аргументов не позволяет религия?

Добавлено
как бы protected\private методы и существуют для того, чтоб не превращать методы в километровый склад кода

Добавлено
Цитата D_KEY @
Покажи его вариант, который был бы лучше варианта с goto

мне все три варианта понятны, ибо смысла в них нет, достаточно одного цикла от 0 до 10 * 10 * 10

Добавлено
Не, не 10*10*10, там стоит инициализация не только нулём. Всё равно один фиг, проще было бы одним циклом



фигню несу :whistle:

Автор: Славян 09.05.15, 10:29
Цитата Serafim @
PHP Fatal error: 'goto' into loop or switch statement is disallowed
:lool: Так вам и надо, лузеры! :good:

Автор: Serafim 09.05.15, 10:36
во, теперь точно рабочий вариант на корутинах и лямбдах:
deleted

Добавлено
или не, блин, опять фейл

Добавлено
щаща, подождите, я докажу, что goto зло

Автор: Славян 09.05.15, 10:43
Цитата Serafim @
подождите, я докажу, что goto зло
Доказательства бывают только в математике, а в программирование внесён огромный пласт культуры=искусства, посему у вас не получится. :D Но всё же подождём! ;)

Автор: Serafim 09.05.15, 10:49
у меня лучше второго варианта не получается =(

Автор: DarkEld3r 09.05.15, 11:16
По моему, второй вариант и так лучше. Наглядность не страдает, а строк кода даже меньше.

Автор: Qraizer 09.05.15, 13:34
Лучше с goto, потому что пишется за три секунды и работает без отладки. При этом прекрасно понятен. Что ещё нужно-то?

Автор: DarkEld3r 09.05.15, 14:40
Не вижу принципиальной разницы со вторым вариантом. Почему его дольше отлаживать или писать? Принцип-то один и тот же. Третий - да, смотрится страшновато, но имхо, он просто неудачный вариант второго.

Не являюсь фанатичным противником goto, если что. Просто как-то так получается, что он и не пригождается. Разве что в генерённом коде может быть полезно. И приведённый пример совсем не убеждает, но подозреваю, что во мнении мы не сойдёмся. :D
Кстати, в расте goto нет (именованные break/continue имеются).

Автор: Славян 09.05.15, 15:08
Допускаю такую канитель, хоть самому никогда и не попадалось такое писать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int myFunc(...)
    {
        int i,j=-1,k;
        // ещё вагоны переменных
        float p,q,r;
        // тело ниже:
        for( i=0; i<10; i++)
        {
          ...
          if( A ) // вызов длинной и сложной функции
          {
            goto Label_ФункцияЯкобы;
           Label_Возврат1:;
          }
        }
        ... // правка вагона переменных
        for( j=0; j<100; j++)
        {
          ...
          if( B ) // вызов длинной и сложной функции
          {
            goto Label_ФункцияЯкобы;
           Label_Возврат2:;
          }
        }
        ... // правка2 вагона переменных
        return 0;
      Label_ФункцияЯкобы:
        ... // сложный кусок, очень нуждающийся в вагоне переменных из этой функции
        if( j<0 ) goto Label_Возврат1;
        goto Label_Возврат2;
    }

Автор: Qraizer 09.05.15, 15:25
А я на практике этот код рожал, DarkEld3r. Пока допёр до структурного варианта, прошло три итерации правки сырцов и отладки. Вопрос: ну и нахрена было? И это я ещё не спрашиваю о стоимости структурного решения в единицах производительности по сравнению с goto. Предполагается-то ведь, что прерывание исполнения и сохранение состояние с последующим восстановлением состояния и продолжением исполнения суть очень редкая операция. Зачем делать лишние движения на каждой итерации ради исчезающе малого процента ситуаций, их обосновывающих?
Ну нет, Славян, вот как раз это я бы перепроектировал, тут с передачами управления полная ахинея.

Автор: Славян 09.05.15, 15:33
Цитата Qraizer @
вот как раз это я бы перепроектировал, тут с передачами управления полная ахинея.
А мне показалось, что всё довольно прозрачно. :yes-sad:

Автор: Qraizer 09.05.15, 16:30
Когда написано, да. :D

Автор: applegame 09.05.15, 16:39
Цитата Serafim @
передать в качестве аргументов не позволяет религия?
Зачем? Мне религия все позволяет, а вот тебе похоже как раз религия и запрещает goto :)

Автор: Serafim 09.05.15, 18:49
Цитата applegame @
Мне религия все позволяет, а вот тебе похоже как раз религия и запрещает goto

верно :yes:

Автор: D_KEY 09.05.15, 20:35
Славян, я уже не очень хорошо помню, но по-моему примерно как-то так я в бейсике и делал процедуры. Под какой-то вариант спектрума, со встроенным бейсиком.

Добавлено
А, нет, наврал. Не заметил, что у тебя в разные места возвращается. Так я вроде не извращался.

Автор: Славян 09.05.15, 20:50
Цитата D_KEY @
у тебя в разные места возвращается. Так я вроде не извращался.
Я сначала в одно место возвращался, но увидел, что тот блок можно тогда было бы засунуть вместо goto, а потому пришлось придумать 2 места возврата. :blush:

Автор: DarkEld3r 11.05.15, 08:01
Цитата Qraizer @
Предполагается-то ведь, что прерывание исполнения и сохранение состояние с последующим восстановлением состояния и продолжением исполнения суть очень редкая операция. Зачем делать лишние движения на каждой итерации ради исчезающе малого процента ситуаций, их обосновывающих?

Этот момент не понял. Разве не в обоих случаях необходимость восстановления состояния проверяется один раз (до входа в цикл)?
Прерывание, опять же, в обоих вариантах проверяется в каждой итерации, да и как иначе.

Аргумент в духе "я это реально писал, было сложно", извини, но не убеждает. С таким же успехом, могла быть обратная ситуация - в смысле долго и мучительно переделывать на вариант с goto. Потому что я в упор не вижу, что тут goto помогает выиграть. Ну да ладно, это не слишком интересный спор.

Автор: D_KEY 11.05.15, 09:34
Цитата DarkEld3r @
Цитата Qraizer @
Предполагается-то ведь, что прерывание исполнения и сохранение состояние с последующим восстановлением состояния и продолжением исполнения суть очень редкая операция. Зачем делать лишние движения на каждой итерации ради исчезающе малого процента ситуаций, их обосновывающих?

Этот момент не понял. Разве не в обоих случаях необходимость восстановления состояния проверяется один раз (до входа в цикл)?

Нет. Во втором случае еще приходится работать особым образом с счетчиками.

Автор: DarkEld3r 11.05.15, 10:42
Да, был невнимателен. В принципе, это своеобразное подтверждение, что с goto тут всё-таки нагляднее. :D Хотя я бы всё равно не сказал, что так уж сильно.

Автор: JoeUser 11.07.20, 13:32
Как говорится не прошло и пол-года ...

Сижу-бодаюсь об "оплюсовывании" либы libssh2 в плане SFTP-функционала. Наверное тру-прогеры скажут, мол не парься, бери QStateMachine. Может это будет даже по-фэншую. Но задача практическая, время горит, а мне нужен только upload и download, и пока даже без чтений каталогов, атрибутов и прочего. К чему я это все? Прогаю в стиле самой либы (а она си-шная), безбожно тягаю куски кода из примеров. И вот пришло время нормальной обработки ошибок ... и вспомнил я эту тему.

Сперва ступор, ну как-же тут без goto?!! Наваял процедуру, потестил, погонял, ну супер - работает. Да есть goto, стыдно - но есть, получилось вот-так:
Скрытый текст
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // ─────────────────────────────────────────────────────────────────────────────────────────────────
    // sftp_class ► slotLogin
    // ─────────────────────────────────────────────────────────────────────────────────────────────────
    void mov::qt::sftp_class::slotLogin() {
        struct addrinfo hint;
        struct addrinfo *addrs;
        struct sockaddr_in *sin;
        const char *fingerprint;
        struct addrinfo *p;
        int ret;
        bool found;
        sftpError = sftp_error::no;
        if (sftpState == sftp_state::ready && Auth->authState == auth_state::ok) {
            // инициализация библиотеки
            if (libssh2_init(0) != 0) {
                sftpError = sftp_error::libssh2_error;
                return;
            }
            // поиск адреса хоста
            memset(&hint, 0, sizeof(hint));
            hint.ai_flags = AI_NUMERICHOST;
            hint.ai_family = AF_UNSPEC;
            hint.ai_socktype = SOCK_STREAM;
            hint.ai_protocol = IPPROTO_TCP;
            ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
            if (ret == EAI_NONAME) {
                hint.ai_flags = 0;
                ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
            }
            if (ret != 0)               {
                sftpError = sftp_error::resolve_error;
                goto cleanup_libssh2_init;
            }
            found = false;
            for (p = addrs; p != nullptr; p = p->ai_next) {
                if (p->ai_family == AF_INET) {
                    found = true;
                    sin = reinterpret_cast<sockaddr_in *>(p->ai_addr);
                    break;
                }
            }
            if (!found) {
                sftpError = sftp_error::ip4_error;
                goto cleanup_libssh2_init;
            }
            // создание сокета
            sock = socket(AF_INET, SOCK_STREAM, 0);
            if (sock == INVALID_SOCKET) {
                sftpError = sftp_error::socket_error;
                goto cleanup_libssh2_init;
            }
            sin->sin_family = AF_INET;
            sin->sin_port = htons(Port);
            // коннект
            ret = ::connect(sock, (struct sockaddr *)(sin), sizeof(struct sockaddr_in));
            if (ret != 0) {
                sftpError = sftp_error::connect_error;
                goto cleanup_socket;
            }
            // создание сессии
            session = libssh2_session_init();
            if (!session) {
                sftpError = sftp_error::session_error;
                goto cleanup_socket;
            }
            // установка баннера
            if (libssh2_session_banner_set(session, "SSH-2.0-OpenSSH_LIBSSH2_1.9.0") != 0) {
                sftpError = sftp_error::session_error;
                goto cleanup_session;
            }
            // рукопожатие
            if (libssh2_session_handshake(session, sock)) {
                sftpError = sftp_error::handshake_error;
                goto cleanup_session;
            }
            // перевод в неблокируемый режим
            libssh2_session_set_blocking(session, 0);
            // получение отпечатка
            fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
            // авторизация
            if (Auth->authType == auth_type::login) {
                while ((ret = libssh2_userauth_password(
                                                session,
                                                Auth->login.toLocal8Bit().data(),
                                                Auth->password.toLocal8Bit().data()
                                            ))   == LIBSSH2_ERROR_EAGAIN);
                if (ret) {
                    sftpError = sftp_error::auth_error;
                    goto cleanup_session;
                }
            } else {
                while ((ret = libssh2_userauth_publickey_frommemory(
                                                session,
                                                Auth->login.toLocal8Bit().data(),
                                                Auth->login.length(),
                                                Auth->public_array.data(),
                                                Auth->public_array.size(),
                                                Auth->private_array.data(),
                                                Auth->private_array.size(),
                                                Auth->password.toLocal8Bit().data()
                                            )) == LIBSSH2_ERROR_EAGAIN);
                if (ret) {
                    sftpError = sftp_error::auth_error;
                    goto cleanup_session;
                }
            }
            // инициализация ftp-сессии
            do {
                sftp_session = libssh2_sftp_init(session);
                if (!sftp_session) {
                    if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
                        waitsocket(sock, session);
                    else {
                        sftpError = sftp_error::auth_error;
                        goto cleanup_session;
                    }
                }
            } while (!sftp_session);
            // все четко и дерзко
            sftpState = sftp_state::logged_in;
            return;
            // аварийная очистка
        cleanup_session:
            libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing!");
            libssh2_session_free(session);
        cleanup_socket:
            #ifdef WIN32
            closesocket(sock);
            #else
            close(sock);
            #endif
        cleanup_libssh2_init:
            libssh2_exit();
        }
    }

Потом подумал, ну можно же без goto. А то посоны не поймут! Сперва пришло на ум - глобальный cвич. Но возникло немотивированное отвращение. И тут на помощь пришла лямбда, правда пришлось еще объявление метода чуть поправить. Получилось вот так:
Скрытый текст
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // ─────────────────────────────────────────────────────────────────────────────────────────────────
    // sftp_class ► slotLogin
    // ─────────────────────────────────────────────────────────────────────────────────────────────────
    bool mov::qt::sftp_class::slotLogin() {
        struct addrinfo hint;
        struct addrinfo *addrs;
        struct sockaddr_in *sin;
        const char *fingerprint;
        struct addrinfo *p;
        int ret;
        bool found;
        enum class cleanup_point {session, socket, libssh2, none};
        auto cleanup = [&](cleanup_point p, sftp_error e)->bool {
            sftpError = e;
            switch (p) {
                case cleanup_point::session:
                    libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing!");
                    libssh2_session_free(session);
                case cleanup_point::socket:
                    #ifdef WIN32
                    closesocket(sock);
                    #else
                    close(sock);
                    #endif
                case cleanup_point::libssh2:
                    libssh2_exit();
                default:;
            }
            return false;
        };
        sftpError = sftp_error::no;
        if (sftpState == sftp_state::ready && Auth->authState == auth_state::ok) {
            // инициализация библиотеки
            if (libssh2_init(0) != 0) return cleanup(cleanup_point::none, sftp_error::libssh2_error);
            // поиск адреса хоста
            memset(&hint, 0, sizeof(hint));
            hint.ai_flags = AI_NUMERICHOST;
            hint.ai_family = AF_UNSPEC;
            hint.ai_socktype = SOCK_STREAM;
            hint.ai_protocol = IPPROTO_TCP;
            ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
            if (ret == EAI_NONAME) {
                hint.ai_flags = 0;
                ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
            }
            if (ret != 0)   return cleanup(cleanup_point::libssh2, sftp_error::resolve_error);
            found = false;
            for (p = addrs; p != nullptr; p = p->ai_next) {
                if (p->ai_family == AF_INET) {
                    found = true;
                    sin = reinterpret_cast<sockaddr_in *>(p->ai_addr);
                    break;
                }
            }
            if (!found) return cleanup(cleanup_point::libssh2, sftp_error::ip4_error);
            // создание сокета
            sock = socket(AF_INET, SOCK_STREAM, 0);
            if (sock == INVALID_SOCKET) return cleanup(cleanup_point::libssh2, sftp_error::socket_error);
            sin->sin_family = AF_INET;
            sin->sin_port = htons(Port);
            // коннект
            ret = ::connect(sock, (struct sockaddr *)(sin), sizeof(struct sockaddr_in));
            if (ret != 0) return cleanup(cleanup_point::socket, sftp_error::connect_error);
            // создание сессии
            session = libssh2_session_init();
            if (!session) return cleanup(cleanup_point::socket, sftp_error::session_error);
            // установка баннера
            if (libssh2_session_banner_set(session, "SSH-2.0-OpenSSH_LIBSSH2_1.9.0") != 0)
                return cleanup(cleanup_point::session, sftp_error::session_error);
            // рукопожатие
            if (libssh2_session_handshake(session, sock))
                return cleanup(cleanup_point::session, sftp_error::handshake_error);
            // перевод в неблокируемый режим
            libssh2_session_set_blocking(session, 0);
            // получение отпечатка
            fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
            // авторизация
            if (Auth->authType == auth_type::login) {
                while ((ret = libssh2_userauth_password(
                                                session,
                                                Auth->login.toLocal8Bit().data(),
                                                Auth->password.toLocal8Bit().data()
                                            ))   == LIBSSH2_ERROR_EAGAIN);
                if (ret) return cleanup(cleanup_point::session, sftp_error::auth_error);
            } else {
                while ((ret = libssh2_userauth_publickey_frommemory(
                                                session,
                                                Auth->login.toLocal8Bit().data(),
                                                Auth->login.length(),
                                                Auth->public_array.data(),
                                                Auth->public_array.size(),
                                                Auth->private_array.data(),
                                                Auth->private_array.size(),
                                                Auth->password.toLocal8Bit().data()
                                            )) == LIBSSH2_ERROR_EAGAIN);
                if (ret) return cleanup(cleanup_point::session, sftp_error::auth_error);
            }
            // инициализация ftp-сессии
            do {
                sftp_session = libssh2_sftp_init(session);
                if (!sftp_session) {
                    if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
                        waitsocket(sock, session);
                    else
                        return cleanup(cleanup_point::session, sftp_error::auth_error);
                }
            } while (!sftp_session);
            // все четко и дерзко
            sftpState = sftp_state::logged_in;
            return true;
        }
        return false;
    }

Вроде и от goto избавился, и код стал короче ... но радости это не прибавило! :-? Читабельность стала хуже. И вот я подумываю вернуться к первой версии реализации слота. Ухожу я от вас, хейтеры goto! :lol:

Автор: korvin 11.07.20, 13:54
JoeUser, что за бред? Пос++оны освобождают ресурсы в деструкторах

Автор: JoeUser 11.07.20, 13:59
Цитата korvin @
Пос++оны освобождают ресурсы в деструкторах

Ага! Давай начинай мне советовать эту портянку с линейным выполнением переписать в 9 классов! :popcorn:

Автор: Wound 11.07.20, 14:38
Цитата korvin @
JoeUser, что за бред? Пос++оны освобождают ресурсы в деструкторах

Ну да! На крайняк можно юзнуть деструктор смарт поинтера, например там: https://ideone.com/GALIGw
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        #include <iostream>
        #include <memory>
        
        
        void slotLogin()
        {
            auto socket_cleanup = [](int* ptr){
                std::cout << "socket_cleanup is called" << std::endl;
            };
        
            auto libssh2_init_cleanup = [](int* ptr){
                std::cout << "libssh2_init_cleanup is called" << std::endl;
            };
        
        auto socket = std::unique_ptr<int, decltype(socket_cleanup)>(new int()/*::connect(...)*/, socket_cleanup);
        auto libssh2_init = std::unique_ptr<int, decltype(libssh2_init_cleanup)>(new int()/*libssh2_init(...)*/, libssh2_init_cleanup);
     
    /*...*/
        }
        
        int main() {
            slotLogin();
            return 0;
        }

И никаких goto не надо.

Автор: JoeUser 11.07.20, 14:46
Цитата Wound @
auto socket = std::unique_ptr<int, decltype(socket_cleanup)>(new int() ..................

Это реально читабельнее goto? И мне для этого обязательно кучу дергать? :lol: Кроме зашкаливающей ненависти к goto я не вижу тут профита.

Автор: Wound 11.07.20, 14:49
Если ты не видишь разницы между этим кодом и своим с goto, а так же если ты не видишь профита, то тут бессмысленно что либо объяснять. Пусть тебе кто то другой растолкует. Мне лень расписывать основы.

Автор: JoeUser 11.07.20, 14:51
Договорились! :) Peace! :victory:

Автор: Wound 11.07.20, 15:21
Цитата JoeUser @
Это реально читабельнее goto?

К слову - этот вопрос тебе нужно адресовать тем кто сидит в комитете по стандартизации. Спроси у них, какого хера - они из С++ делают какое то вырвиглазное говно, на котором нельзя нормально писать читабельный код, что его даже не осиливают люди с 20тилетним стажем, теряются в двух строчках шаблонных параметров. Понавводили тут панимаешь ли, всякие вариадики, лямбды... :lol:

Автор: OpenGL 11.07.20, 16:17
Цитата JoeUser @
Давай начинай мне советовать эту портянку с линейным выполнением переписать в 9 классов!

Где 9? Я тут только три вижу, по количеству cleanup-ов. И в чём сложость? Классы эти всего лишь raii обёрткой будут, вообще ни о чём.
Но вообще в целом да, если ты пишешь программу из 100 строк, то выносить в ней освобождение ресурсов в raii если ты юзаешь сишное api смысла действительно не сильно много.

Цитата JoeUser @
Кроме зашкаливающей ненависти к goto я не вижу тут профита.

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

Автор: JoeUser 11.07.20, 16:50
Цитата OpenGL @
А он есть. Как минимум это устойчивость программы к неожиданным исключениями и ошибкам в последующих изменениях.

Только не здесь! Дело в том, что я ее пилю под API Qt5, а тамошние прогеры не жалуют исключения, более того - советуют про них вааще забыть, если используется Qt. Ну с ними конечно не очень согласен, я вааще ни с кем не согласен, но с ними - больше не согласен! :) Но вынужден внемлить.

Автор: OpenGL 11.07.20, 17:11
Это всё не отменяет остальное. Ок, исключения ты не юзаешь, но все эти многочисленные goto cleanup в разы более подвержены ошибкам и в разы менее устойчивы, чем нормальное решение.
Хотя я это уже не раз объяснял, причём даже тебе. Если ты до сих пор задаёшься вопросом, зачем это надо, то смысла вдаваться в подробности не очень много.

Автор: JoeUser 11.07.20, 17:37
Цитата OpenGL @
в разы менее устойчивы, чем нормальное решение

OpenGL, че мы будем переливать из пустого в порожнее? Типа "там это вам не тут". Давай на примере покажи! Вот тебе кусок подобного кода, Всего 49 строк. Трансформируй его, как ты считаешь правильным, и мы вместе с тобой посмотрим?

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // lv = local value
    // cv = class value
    // r1 .. r3 - захватываемые ресурсы
     
    lv = init1(r1);
    if (lv!=0) {
      err = "err-1";
      goto cleanup1;
    }
    cv1 = init2(r2);
    if (cv1!=0) {
      err = "err-2";
      goto cleanup3;
    }
    lv = init3();
    if (lv!=0) {
      cv1 = -1;
      err = "err-3";
      goto cleanup2;
    }
    cv2 = init4();
    if (cv2!=0) {
      err = "err-4";
      goto cleanup2;
    }
    lv = init5();
    if (lv!=0) {
      err = "err-5";
      goto cleanup3;
    }
    lv = init6(r3);
    if (lv!=0) {
      err = "err-6";
      goto cleanup4;
    }
     
    ....
    ....
    ....
    return;
     
    cleanup4:
    release3(r3);
    cleanup3:
    cv1 = 0;
    cv2 = 0;
    cleanup2:
    release2(r2);
    cleanup1:
    release1(r1);

Автор: OpenGL 11.07.20, 18:09
Зачем мне как-то трансформировать код, который ты только что придумал специально так, чтобы его было удобно писать именно с goto? :D Он не подобен, т.к. у тебя тут goto cleanup%random% идёт. На практике же такое не встречается, и, в частности, твой оригинальный код прекрасно разруливается через деструкторы.

Добавлено
Да и вообще - что ты доказываешь? Если то, что goto иногда полезен, то я с этим тезисом скорее согласен, однако твой код это не доказывает.

Автор: JoeUser 11.07.20, 18:31
Цитата OpenGL @
Зачем мне как-то трансформировать код, который ты только что придумал специально так, чтобы его было удобно писать именно с goto? Он не подобен, т.к. у тебя тут goto cleanup%random% идёт. На практике же такое не встречается, и, в частности, твой оригинальный код прекрасно разруливается через деструкторы.

Добавлено 3 минуты назад
Да и вообще - что ты доказываешь? Если то, что goto иногда полезен, то я с этим тезисом скорее согласен, однако твой код это не доказывает.

Ответ на пять! :good: 49 совершенно неправильных строчек - лишили меня дозы адреналина :'( А он, окаянный, так благодатно выделяется как раз в моменты, когда тебя такого самоуверенного тыкают носом в невиданной красоты код. А тут ... опять облом. И уже стандартная аргументация - "никогда такого не было - и вот опять!" (С)

Добавлено
Цитата OpenGL @
однако твой код это не доказывает

Ну покажи как правильно на моем коде! И моя благодарность будет безгранична в разумных пределах.

Автор: OpenGL 11.07.20, 18:40
Цитата JoeUser @
Ну покажи как правильно на моем коде!

У тебя естественным образом выделяются 3 ресурса, и их освобождение привязано к трём меткам. Сделай создание соответствующих обёрток над ними в нужные моменты (хоть объяви типы руками, хоть так, как Киля показал - пофиг), в моменты, когда что-то идёт не так, делай просто return. А когда ты дошёл до самого конца, просто делай std::move этих ресурсов в поле своего класса.

Автор: JoeUser 11.07.20, 18:59
OpenGL, не нужно слов - покажи кодом! Я же все выложил - для копипасты нет пределов. Мне просто хочется тупо сравнить мое говтушное :) , и твое красивое. Сразу предупреждаю - отмазы типа "мне лень" воспринимаются как трамвайный съезд. Как говорят "сказал А - говори и Бэ". Мне нужно сравнить два кода, мой говнявый, и твой эталонный. Не будет кода - разговор закончен, ибо пока это - вода.
Скрытый текст
Напомню - у нас сайт называется "исходники" а не "водолеи"

Автор: korvin 11.07.20, 19:05
JoeUser, нормальные посоны написали бы так (если прям нужно без исключений):

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    error* init(R1& r1, R2& r2, R3& r3)
    {
        LV lv;
        lv = init1(&r1);
        if (lv != 0) {
            return "err-1";
        }
        cv1 = init2(&r2);
        if (cv1 != 0) {
            return "err-2";
        }
        lv = init3();
        if (lv != 0) {
            cv1 = -1;
            return "err-3";
        }
        cv2 = init4();
        if (cv2 != 0) {
            return "err-4";
        }
        lv = init5();
        if (lv != 0) {
            return "err-5";
        }
        lv = init6(&r3);
        if (lv != 0) {
            return "err-6";
        }
        return null;
    }
     
    error* init()
    {
        R1 r1;
        R2 r2;
        R3 r3;
     
        error* err = init(&r1, &r2, &r3);
        if (err != null) {
            cv1 = 0;
            cv2 = 0;
            return err;
        }
        ...
        ...
        ...
        return null;
    }

Автор: Wound 11.07.20, 19:25
Цитата JoeUser @
OpenGL, че мы будем переливать из пустого в порожнее? Типа "там это вам не тут". Давай на примере покажи! Вот тебе кусок подобного кода, Всего 49 строк. Трансформируй его, как ты считаешь правильным, и мы вместе с тобой посмотрим?

Если функции которые используются в этом говнокоде реально делают полезную работу, например там - открывают файлы, выделяют память, устанавливают соединие и т.п. То в принципе в этом говнокоде как минимум 3 утечки. Если же они просто возвращают какие то переменные на стеке, то их уничтожать смысла нет.
ЗЫЖ
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    lv = init1(r1); //! <<<<<<<<<<<<<<<<<<< Выделили память в куче, открыли файл, установили соединение, и т.д.
    if (lv!=0) {
      err = "err-1";
      goto cleanup1;
    }
    cv1 = init2(r2);
    if (cv1!=0) {
      err = "err-2";
      goto cleanup3;
    }
    lv = init3(); //! <<<<<<<<<<<<<<<<<<<<< плевать что перед этим мы завладели ресурсом, в этом месте мы теряем ранее захваченный ресурс.

Ну и т.д. по коду. Ну а если ты всего лишь опечатался и там другие имена переменных, то этот говнокод вполдне себе переписывается в нормальный, качественный код: https://ideone.com/WMrtkA

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
                #include <iostream>
               #include <memory>
        
        
                void SomeFunc()
                {
                    auto r1_cleanup = [](int* ptr){
                        std::cout << "r1_cleanup is called" << std::endl;
                    };
        
                    auto r2_cleanup = [](int* ptr){
                        std::cout << "r2_cleanup is called" << std::endl;
                    };
        
                    auto r3_cleanup = [](int* ptr){
                        std::cout << "r3_cleanup is called" << std::endl;
                    };
        
                auto lv = std::unique_ptr<int, decltype(r1_cleanup)>(new int()/*::init1(...)*/, r1_cleanup);
                auto cv1 = std::unique_ptr<int, decltype(r2_cleanup)>(new int()/*::init2(...)*/, r2_cleanup);
                auto cv2 = std::unique_ptr<int, decltype(r3_cleanup)>(new int()/*::init3(...)*/, r3_cleanup);
        
            /*...*/
                }
        
                int main() {
                    SomeFunc();
                    return 0;
                }

Автор: JoeUser 11.07.20, 19:27
Цитата korvin @
(если прям нужно без исключений)

Неа ...
Скрытый текст
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    error* init(R1& r1, R2& r2, R3& r3)
    {
        LV lv;
        lv = init1(&r1);
        if (lv != 0) {
            return "err-1";
        }
        cv1 = init2(&r2);  
        if (cv1 != 0) {
            return "err-2";
        }
        lv = init3();
        if (lv != 0) {  ◄─── тут случился облом, нужно освободить r1, r2, а cv1=-1, ─┐
            cv1 = -1;                                                                │
            return "err-3";                                                          │
        }                                                                            │
        cv2 = init4();                                                               │
        if (cv2 != 0) {                                                              │
            return "err-4";                                                          │
        }                                                                            │
        lv = init5();                                                                │
        if (lv != 0) {                                                               │
            return "err-5";                                                          │
        }                                                                            │
        lv = init6(&r3);                                                             │
        if (lv != 0) {                                                               │
            return "err-6";                                                          │
        }                                                                            │
        return null;                                                                 │
    }                                                                                │
                                                                                     │
    error* init()                                                                    │
    {                                                                                │
        R1 r1;                                                                       │
        R2 r2;                                                                       │
        R3 r3;                                                                       │
                                                                                     │
        error* err = init(&r1, &r2, &r3);                                            │
        if (err != null) {                                                           │
            cv1 = 0;    ◄───── а ты? ────────────────────────────────────────────────┘
            cv2 = 0;
            return err;
        }
        ...
        ...
        ...
        return null;
    }


Добавлено
Цитата Wound @
Выделили память в куче,

С хера ли "выделили"??? Функция вернула указатель в мою стековую переменную на данные из своих "недр".
Цитата Wound @
плевать что перед этим мы завладели ресурсом, в этом месте мы теряем ранее захваченный ресурс.

Lv - не указатель, а флаг успешности/неуспешности отработки функции Init<номер>. Вааще не теряется, не теряется совсем-совсем! Честное слово!
Цитата Wound @
в этом говнокоде

Ты как чукча, о чем вижу - о том пою! :lool: Киля ты отвечал не на мой код, а на тот, который сам себе придумал - и осудил! (Кстати. Я - тебя поддерживаю!!!)

Автор: Wound 11.07.20, 19:42
Цитата JoeUser @
С хера ли "выделили"??? Функция вернула указатель в мою стековую переменную на данные из своих "недр".

Откуда я знаю что эта портянка говнокода, которую ты привел, делает ? Вот это как раз образец нечитаемой херни, которую даже студенты забраковали бы!

Цитата JoeUser @
Lv - не указатель, а флаг успешности/неуспешности отработки функции Init<номер>. Вааще не теряется, не теряется совсем-совсем! Честное слово!

Да ладно? Сурьезно? А скажи ка, что делают функции init3(); init4(); init5(); ? И как ты определил что после неуспешного вызова init3 и init4 нужно сделать goto cleanup2;, а после неуспешного вызова init5 нужно сделать cleanup3 ? Я же говорю, из этого говнокода - ничего не понять, эталон говна, так сказать. Ты его сначало перепиши так, чтоб он хоть както читался и в нем была хоть какая то логика, а потом уже спрашивай как это улучшить.

Добавлено
Цитата JoeUser @
Ты как чукча, о чем вижу - о том пою! :lool: Киля ты отвечал не на мой код, а на тот, который сам себе придумал - и осудил! (Кстати. Я - тебя поддерживаю!!!)

Ну так в этом говне что ты привел, кроме тебя наверное никто не разберется, потому как одному тебе понятно как оно работает(на самом деле это дерьмо не будет работать в таком виде, а если его закомитить в прод - то скорее всего вызовут на ковер с возможным увольнением по статье профнепригодность).

Автор: korvin 11.07.20, 19:51
Цитата JoeUser @
Неа ...

Что «неа»?

Цитата JoeUser @
тут случился облом, нужно освободить r1, r2

r1, r2 и r3 гарантированно освобождаются при выходе из процедуры init()

Цитата JoeUser @
а cv1=-1

Да пожалуйста
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    error* init(R1& r1, R2& r2, R3& r3)
    {
        LV lv;
        lv = init1(&r1);
        if (lv != 0) {
            return "err-1";
        }
        cv1 = init2(&r2);
        if (cv1 != 0) {
            set_zero();
            return "err-2";
        }
        lv = init3();
        if (lv != 0) {
            cv1 = -1;
            return "err-3";
        }
        cv2 = init4();
        if (cv2 != 0) {
            return "err-4";
        }
        lv = init5();
        if (lv != 0) {
            set_zero();
            return "err-5";
        }
        lv = init6(&r3);
        if (lv != 0) {
            set_zero();
            return "err-6";
        }
        return null;
    }
     
    error* init()
    {
        R1 r1;
        R2 r2;
        R3 r3;
     
        error* err = init(&r1, &r2, &r3);
        if (err != null) {
            return err;
        }
        ...
        ...
        ...
        return null;
    }
     
    void set_zero()
    {
        cv1 = 0;
        cv2 = 0;
    }

Автор: Wound 11.07.20, 19:59
Цитата JoeUser @
Киля ты отвечал не на мой код, а на тот, который сам себе придумал - и осудил!

К слову мой пример подходит даже к тому говнокоду, который ты сам себе придумал, и который возможно я даже не так понял.
Тебе бы почитать про RAII - основного кита С++, может быть малеха умнее стал, но судя по уровню приведенного кода - я даже не знаю, поймешь ли ты основную идею этой идеологии. :-?
На самом деле что предыдущий пример что этот - это как раз реализация RAII в чистом виде, unique_ptr - нужен лишь с единственной целью, чтоб не плодить классы, в него очень удобно оборачивать сишные функции, которые завладевают ресурсами и которые в последствии необходимо освободить.
Какой профит от этого? Профит в том, что теперь тебе не нужно думать когда и в каком месте надо вызвать goto или cleanup, ресурс сам знает когда должен уничтожится и управляет временем его жизни и освобождением ресурсов - язык. А значит это более надежнее.

Автор: JoeUser 11.07.20, 20:02
Цитата Wound @
Откуда я знаю что эта портянка говнокода, которую ты привел, делает ?

Вот это самый важный вопрос!!! "Откуда я знаю что эта портянка говнокода, которую ты привел, делает ?" - но советую! Ты - кросава!!! :lool: Киля, запомни, и расскажи всем родным и близким - "диавол кроется в мелочах"!

Цитата Wound @
эталон говна, так сказать

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

Цитата Wound @
Ну так в этом говне что ты привел, кроме тебя наверное никто не разберется, потому как одному тебе понятно как оно работает

И это все потому, что - Я ЛУЧШИЙ в говне!

Цитата Wound @
то скорее всего вызовут на ковер с возможным увольнением по статье профнепригодность

Я уже 10 лет работаю сам на себя. Максимум - расстреляют, но я не лезу в политику, не митингую. Ну если только изредка, устраиваю молчаливый пикет с вискарем - дома 8-) Мечтаю съездить в Париж, поссать с Эйфелефой башни, ибо построить такую мне мешает Путин!

Добавлено
korvin, завтра повникаю ... а пока обоснуй - почему в твоем коде 56 строк, а в моем 49 - и ты что, качество "построчно" оцениваешь? Нахрена раздул код?!! :)

Автор: Wound 11.07.20, 20:11
Цитата JoeUser @
Вот это самый важный вопрос!!! "Откуда я знаю что эта портянка говнокода, которую ты привел, делает ?" - но советую! Ты - кросава!!! :lool: Киля, запомни, и расскажи всем родным и близким - "диавол кроется в мелочах"!

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

Цитата JoeUser @
Ошибаешься, это - эталон эталонов говна! Ну бывают такие взаимосвязи в состояниях систем, что да - хочется срать говном. Но если на кону зарплата/гонорар - засунь себе уникальный поинтер в жопу, но просто сделай, чтобы работало.

:lool: Это так ты решил оправдать свой пример, которому просил хорошую альтернативу, а все идеи тут же раскритиковал? :D Я такое даже в пьяном угаре бы не смог написать под дулом пистолета. Потому что в этом запутаться можно по ходу написания этого говна.

Цитата JoeUser @
И это все потому, что - Я ЛУЧШИЙ в говне!

С чем тебя и поздравляю!

Цитата JoeUser @
Я уже 10 лет работаю сам на себя. Максимум - расстреляют, но я не лезу в политику, не митингую. Ну если только изредка, устраиваю молчаливый пикет с вискарем - дома 8-) Мечтаю съездить в Париж, поссать с Эйфелефой башни, ибо построить такую мне мешает Путин!

В С++ идиома RAII появилась вместе с появлением классов и деструкторов. Т.е. ей больше 10 лет, идея простая и безотказная как топор, очень странно видеть людей, которые пишут вроде на С++, но с основной его идеей не знакомы, и пытаются выдумать какую то чушь, используя всякий бред типа goto там, где ему не место.

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

Автор: JoeUser 11.07.20, 20:18
korvin, пока я не заснул - давай переписывай! :lol:

Следи за движениями:

1) lv = init1(&r1); // произошла инициализация USB-драников
2) cv1 = init2(&r2); // попытка инициализации USB-бетономешалки, но она сломалась в обед!

Не удается залить соседа бетоном - облом!!! Мож хоть драников поедим??? Но, чудя по твоему коду, мы не дождемся драников - release2(r1); Короче, драники отдавай, да?!

Автор: JoeUser 11.07.20, 20:33
Цитата Wound @
а все идеи тут же раскритиковал?

И ничего я не критиковал! И вааще всем сенксы, что тратите время на меня. А если будет альтернативный (эталонно-качественный код) - гарантирую УВАЖУХУ!!!

Цитата Wound @
Я такое даже в пьяном угаре бы не смог написать под дулом пистолета. Потому что в этом запутаться можно по ходу написания этого говна.

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

Цитата Wound @
В С++ идиома RAII появилась вместе с появлением классов и деструкторов. Т.е. ей больше 10 лет, идея простая и безотказная как топор, очень странно видеть людей, которые пишут вроде на С++, но с основной его идеей не знакомы, и пытаются выдумать какую то чушь, используя всякий бред типа goto там, где ему не место.

Полностью с тобой согласен!

Цитата Wound @
Ты ведь должен понимать что твои примеры - нечитабельны от слова совсем, плохо подвержены рефакторингу, не устойчивы к различным ошибкам, и сами являются кладезью подводных камней? Хотя о чем это я....

Не согласен вот тут с тобой совсем!!!

GOTO - КЛАССНАЯ ВЕЩЬ!!! Это - раритет, это олд-сулл, это модно, спортивно и УЖЕ молодежно!!!

Или не? ;)

Автор: Wound 11.07.20, 20:41
Цитата JoeUser @
Молод ты еще - прокачивай IQ. Это пригодиться для оценки нестандартных систем. Да и говна в голове будет меньше, ибо - ты познаешь дзен

Хорошая отмазка :lol:


Цитата JoeUser @
Полностью с тобой согласен!

А приводишь говно какое то, и еще кичишься, когда тебе приводят эталонные решения.

Цитата JoeUser @
Не согласен вот тут с тобой совсем!!!

Это от того что ты не работаешь с крупными проектами. Пишешь себе там как ИП, сам потом свое же и ковыряешь. Я многого повидал, но таких говнокодов не видел.

Цитата JoeUser @
GOTO - КЛАССНАЯ ВЕЩЬ!!! Это - раритет, это олд-сулл, это модно, спортивно и УЖЕ молодежно!!!

Ну писать говно подобное тому что ты выше приводил - самое то. Его(goto) можно использовать, но его необходимо использовать с пользой, в твоих примерах пользы от него 0, больше негатива.

Автор: JoeUser 11.07.20, 21:05
Цитата Wound @
приводят эталонные решения

Не, не видел ... один свистеж :lol:

Цитата Wound @
Я многого повидал, но таких говнокодов не видел.

Не сдерживай себя - гони по полной! Эти твари, создатели libssh2 тебя явно недостойны, гавнюки!!!
Я то-типа копипастер (рядом стоял), это все они - недостойные!!!

Цитата Wound @
Ну писать говно подобное тому что ты выше приводил - самое то

Просто у тебя не достаточно опята, чтобы осознать всю красоту ПРЕКРАСНОГО GOTO!!!
Просто не дорос, не прочувствовал, не осознал. Не парься - все это лечит время.

Автор: Wound 11.07.20, 21:10
Цитата JoeUser @
Не, не видел ... один свистеж :lol:

Ты их критиковал! А теперь говоришь - не видел. :lol:

Цитата JoeUser @
Не сдерживай себя - гони по полной! Эти твари, создатели libssh2 тебя явно недостойны, гавнюки!!!
Я то-типа копипастер (рядом стоял), это все они - недостойные!!!

Причем тут они? Ты их код привел? И чего? Он от этого лучше не стал, как был говном - так и остался.
Что ты сказать то хотел, я не понимаю? Ровно такого же кода тонны можно встретить в MSDN - писали там индусы, видимо libssh2 писали тоже индусы :-?

Цитата JoeUser @
Просто у тебя не достаточно опята, чтобы осознать всю красоту ПРЕКРАСНОГО GOTO!!!
Просто не дорос, не прочувствовал, не осознал. Не парься - все это лечит время.

Да? А мне кажется это ты деградировал до уровня, лепить goto туда где он нафиг не упал.

Автор: D_KEY 11.07.20, 21:11
JoeUser, плохой пример. Прочти про RAII и попробуй сам набросать код. Потом можно тут обсудить будет.

Сразу замечу, что обертку нужно писать(или не нужно, если использовать умные указатели) не на каждый объект ресурса, а только на каждый тип, после чего обертка может быть использована везде, где идет работа с этим ресурсом, и код станет гораздо чище.

Автор: JoeUser 11.07.20, 21:19
Цитата D_KEY @
oeUser, плохой пример. Прочти про RAII и попробуй сам набросать код. Потом можно тут обсудить будет.

Сразу замечу, что обертку нужно писать(или не нужно, если использовать умные указатели) не на каждый объект ресурса, а только на каждый тип, после чего обертка может быть использована везде, где идет работа с этим ресурсом, и код станет гораздо чище.

Ага! Я за все это время не смог прочитать про RAII, и не смог его стопицот раз осознать?!! Ты это серьезно???

А я тебе предлагаю переработать свой код - и показать всю красоту красот. Есть и реальный код, есть и "синтетика". Все есть в обсуждении - жду пруфы в виде красивого кода. Без кода - буквы читать не буду. Нафик.

Автор: Wound 11.07.20, 21:20
Цитата JoeUser @
А я тебе предлагаю переработать свой код - и показать всю красоту красот. Есть и реальный код, есть и "синтетика". Все есть в обсуждении - жду пруфы в виде красивого кода. Без кода - буквы читать не буду. Нафик.

Так ты расскажи что в моем коде не так? Ты код korvin что то там критиковал, а на мой не обратил внимания?

Добавлено
Мой код займет меньше твоего по количеству строчек. Туда еще три строчки добавить только осталось чтоб полностью имтировать твой пример ;)

Автор: D_KEY 11.07.20, 21:31
Цитата JoeUser @
Ага! Я за все это время не смог прочитать про RAII, и не смог его стопицот раз осознать?!!

Я не могу знать, что ты там осознал :)
Но пишешь ты так, как будто не понял, да.

Добавлено
Цитата JoeUser @
А я тебе предлагаю переработать свой код - и показать всю красоту красот.

Ты имеешь в виду свой код с goto отсюда?
Ну хорошо, приведу на днях свой вариант.

Автор: Qraizer 12.07.20, 00:06
Цитата JoeUser @
49 совершенно неправильных строчек - лишили меня дозы адреналина
Видишь ли, JoeUser, ты начал с очень известного и прекрасно себя зарекомендовавшего паттерна для C, который, ввиду того, что переписываешь его на C++, естественно тебе не понравился, ибо в Плюсах его можно переписать архитектурно иначе, и с тобой по этому поводу начали дискуссию, а тут синтезировал совсем другой код, под исходный очень известный и прекрасно себя зарекомендовавший паттерн для C не попадающий. Почему у начавших дискуссию нет желания им заниматься, думаю, удивляться не стоит.

Автор: OpenGL 12.07.20, 05:41
JoeUser, у нормальных плюсовиков это будет выглядеть хотя бы так
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // Ресурс libssh_init/libssh_exit
    struct SshInit
    {
        bool initialized;
        SshInit(bool init_flag = false) : initialized(init_flag) {}
     
        SshInit(SshInit &&old) noexcept { old.initialized = false; }
     
        SshInit &operator=(SshInit &&old) noexcept { old.initialized = false; }
     
        ~SshInit()
        {
            if (initialized) libssh2_exit();
        }
    };
     
    // Ресурс socket/close
    struct Socket
    {
        int socket;
        Socket(int s = -1) : socket(s) {}
     
        Socket(Socket &&old) noexcept
        {
            socket     = old.socket;
            old.socket = -1;
        }
        Socket &operator=(Socket &&old) noexcept
        {
            deinit();
            socket     = old.socket;
            old.socket = -1;
            return *this;
        }
     
        ~Socket() { deinit(); }
     
        void deinit()
        {
            if (socket != -1)
            {
    #ifdef WIN32
                closesocket(sock);
    #else
                close(socket);
    #endif
            }
        }
    };
     
    // Ресурс libssh_session_init/libssh_session_free
    struct Session
    {
        LIBSSH2_SESSION *session;
        Session(LIBSSH2_SESSION *s) : session(s) {}
     
        Session(Session &&old) noexcept
        {
            session     = old.session;
            old.session = nullptr;
        }
        Session &operator=(Session &&old) noexcept
        {
            deinit();
            session     = old.session;
            old.session = nullptr;
            return *this;
        }
     
        ~Session() { deinit(); }
     
        void deinit()
        {
            if (session)
            {
                libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing!");
                libssh2_session_free(session);
            }
        }
    };
     
    sftp_error slotLoginInternal()
    {
        struct addrinfo hint;
        struct addrinfo *addrs;
        struct sockaddr_in *sin;
        const char *fingerprint;
        struct addrinfo *p;
        int ret;
        bool found;
     
        // инициализация библиотеки
        if (libssh2_init(0) != 0)
        {
            return sftp_error::libssh2_error
        }
     
        SshInit local_ssh(true);
     
        // поиск адреса хоста
        memset(&hint, 0, sizeof(hint));
        hint.ai_flags    = AI_NUMERICHOST;
        hint.ai_family   = AF_UNSPEC;
        hint.ai_socktype = SOCK_STREAM;
        hint.ai_protocol = IPPROTO_TCP;
        ret              = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
        if (ret == EAI_NONAME)
        {
            hint.ai_flags = 0;
            ret           = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
        }
        if (ret != 0)
        {
            return sftp_error::resolve_error;
        }
     
        found = false;
        for (p = addrs; p != nullptr; p = p->ai_next)
        {
            if (p->ai_family == AF_INET)
            {
                found = true;
                sin   = reinterpret_cast<sockaddr_in *>(p->ai_addr);
                break;
            }
        }
     
        if (!found)
        {
            return sftp_error::ip4_error;
        }
        // создание сокета
        int socket_id = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_id == INVALID_SOCKET)
        {
            return sftp_error::socket_error;
        }
        Socket local_socket(socket_id);
     
        sin->sin_family = AF_INET;
        sin->sin_port   = htons(Port);
        // коннект
        ret = ::connect(sock, (struct sockaddr *)(sin), sizeof(struct sockaddr_in));
        if (ret != 0)
        {
            return sftp_error::connect_error;
        }
        // создание сессии
        LIBSSH2_SESSION *session = libssh2_session_init();
        if (!session)
        {
            return sftp_error::session_error;
        }
        Session local_session(session);
     
        // установка баннера
        if (libssh2_session_banner_set(session, "SSH-2.0-OpenSSH_LIBSSH2_1.9.0") != 0)
        {
            return sftp_error::session_error;
        }
        // рукопожатие
        if (libssh2_session_handshake(session, sock))
        {
            return sftp_error::handshake_error;
        }
        // перевод в неблокируемый режим
        libssh2_session_set_blocking(session, 0);
        // получение отпечатка
        fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
        // авторизация
        if (Auth->authType == auth_type::login)
        {
            while ((ret = libssh2_userauth_password(session, Auth->login.toLocal8Bit().data(),
                                                    Auth->password.toLocal8Bit().data())) ==
                   LIBSSH2_ERROR_EAGAIN)
                ;
            if (ret)
            {
                return sftp_error::auth_error;
            }
        }
        else
        {
            while ((ret = libssh2_userauth_publickey_frommemory(
                        session, Auth->login.toLocal8Bit().data(), Auth->login.length(),
                        Auth->public_array.data(), Auth->public_array.size(),
                        Auth->private_array.data(), Auth->private_array.size(),
                        Auth->password.toLocal8Bit().data())) == LIBSSH2_ERROR_EAGAIN)
                ;
            if (ret)
            {
                return sftp_error::auth_error;
            }
        }
        // инициализация ftp-сессии
        do
        {
            sftp_session = libssh2_sftp_init(session);
            if (!sftp_session)
            {
                if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
                    waitsocket(sock, session);
                else
                {
                    return sftp_error::auth_error;
                }
            }
        } while (!sftp_session);
        // все четко и дерзко
        sftpState = sftp_state::logged_in;
     
        // Сохранение локальных ресурсов в переменные класса
        session  = std::move(local_session);
        socket   = std::move(local_socket);
        ssh_init = std::move(local_ssh);
     
        return sftp_error::no;
    }
     
    void mov::qt::sftp_class::slotLogin()
    {
     
        if (sftpState == sftp_state::ready && Auth->authState == auth_state : k)
        {
            sftpError = slotLoginInternal();
        }
    }

Уже одно это проще в поддержке даже несмотря на то, что тебе нужно учитывать порядок объявления полей ресурсов в классе.

Автор: JoeUser 12.07.20, 11:08
OpenGL, ну идея понятна. Благодаря беседе я у себя недочет нашел. Соответственно он и в твоем коде. Забыл про cleanup соединения. Так что еще класс лепить нужно. Но, если честно, если выбирать между советами - мне предложение Кили больше понравилось.

Автор: D_KEY 12.07.20, 11:18
Цитата JoeUser @
OpenGL, ну идея понятна. Благодаря беседе я у себя недочет нашел. Соответственно он и в твоем коде. Забыл про cleanup соединения. Так что еще класс лепить нужно.

Во-первых, если там указатели, то можно просто воспользоваться unique_ptr/shared_ptr (так из примера OpenGL можно убрать Session и заменить на умный указатель).
Во-вторых, еще раз, такой класс-обертку ты напишешь один раз, а использовать его можно много где (в отличие от goto, который придется писать в каждой подобной функции) и везде твой код (самой функции) станет чище и проще.

Автор: OpenGL 12.07.20, 16:35
Цитата JoeUser @
Но, если честно, если выбирать между советами - мне предложение Кили больше понравилось.

Да ради бога. Мне просто не нравится подобный стиль с deleter-ами, но это исключительно моё субъективное мнение, от проблем же, которые есть в твоём изначальном коде, его решение точно так же защищает.

Автор: JoeUser 13.07.20, 02:22
Цитата OpenGL @
от проблем же, которые есть в твоём изначальном коде,

Погоди, а в моем коде уже проблемы появились? ;)

Автор: Wound 13.07.20, 07:55
Цитата JoeUser @
Погоди, а в моем коде уже проблемы появились?

Одну из них ты даже обнаружил уже тут на форуме :lol:

Добавлено
Цитата JoeUser @
Забыл про cleanup соединения.

Типичная ошибка такого подхода. :-? Посмотри внимательно, может еще где goto забыл впихнуть, а то мало ли...

Добавлено
Цитата JoeUser @
struct addrinfo *addrs;
...
ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);

Память под указатель выделяется, но нигде не освобождается - утечка.
Вот посмотри как ее используют:
https://man7.org/linux/man-pages/man3/getaddrinfo.3.html

Добавлено
А если бы ты не придумывал вот эти вот говновелосипеды с goto, а везде использовал RAII, хоть так, как тебе выше написали (не должно быть у тебя в коде сырых указателей от слова вообще, а у тебя их целая телега) то у тебя бы и утечек не было. А так тут еще можно найти ошибки, если внимательно взглянуть, да всякие подводные камни выползут. И читать портянку такого кода не очень приятно. Тут пол функции - мусора из всяких goto, из за чего тело функции увеличилось в объеме в три раза, оно стало не читабельным, и стремным, кладезью ошибок.

Автор: JoeUser 13.07.20, 10:28
Цитата Wound @
Одну из них ты даже обнаружил уже тут на форуме

RAII тут не помощник! Как с goto, так и с деструктором - можно забыть про необходимость закрытия соединения. Какая разница где забывать? :lol:

Цитата Wound @
Память под указатель выделяется, но нигде не освобождается - утечка.

Вот за это спасибо! Честно говоря понадеялся на 100+ спасибок на один из ответов на SO, и не перепроверил. Но и тут мне RAII бы не подсказал вызвать freeaddrinfo. Так что если бы у нас была тема "RAII vs Киля" - я бы только за тебя и топил бы :lol:

Автор: Wound 13.07.20, 11:01
Цитата JoeUser @
RAII тут не помощник! Как с goto, так и с деструктором - можно забыть про необходимость закрытия соединения. Какая разница где забывать?

Уже был спор на эту тему. Как говорил OpenGL - в С++ утечек нет. RAII - тут как раз таки помощник. Почитай про то, что это такое. Тебе сразу станет ясно(потому что хоть ты когда то и читал про это, но видно что ты плаваешь в этом).
Во первых - если ты юзаешь указатель - и оборачиваешь его в класс/unique_ptr/shared_ptr/etc - в любом случае тебе придется задуматься что писать в деструкторе/делитере, либо за тебя это сделает смарт поинтер.
Во вторых - ты пишешь обертку для ресурса, соответственно забыть написать освобождение - довольно не тривиальная задача, ну разве что ты будешь писать не думая вообще.

Цитата JoeUser @
Вот за это спасибо! Честно говоря понадеялся на 100+ спасибок на один из ответов на SO, и не перепроверил. Но и тут мне RAII бы не подсказал вызвать freeaddrinfo. Так что если бы у нас была тема "RAII vs Киля" - я бы только за тебя и топил бы

Подсказал бы. Иначе указатель небыло бы смысла оборачивать в класс/смарт поинтер. Попробуй избавится от вообще всех голых указателей в своей функции и ты это поймешь сразу же. Да и вообще - где есть указатель, там с очень большой вероятностью выделяется память динамически - значит она где то должна быть освобождена. Я бы еще посмотрел на функцию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);

fingerprint - у тебя const char*, тот же указатель! Может быть там лучше было бы юзнуть std::string? Да и вообще зачем тебе этот fingerprint, он у тебя вообще нигде не используется, только в этой строке.

Автор: JoeUser 13.07.20, 11:52
Цитата Wound @
Уже был спор на эту тему. Как говорил OpenGL - в С++ утечек нет.

1 момент) Я тебе по вызов процедуры закрытия сокета // int shutdown(int sockfd, int how); //, а ты мне про утечки!
2 момент) Как мне RAII напомнит вызвать freeaddrinfo, тем более что выделял не я, а либа winsock2?

Добавлено
Цитата Wound @
fingerprint - у тебя const char*, тот же указатель!

Это скорее всего указатель на кишки libssh2, и они освободятся когда я вызову libssh2_exit();

Автор: Wound 13.07.20, 12:25
Цитата JoeUser @
1 момент) Я тебе по вызов процедуры закрытия сокета // int shutdown(int sockfd, int how); //, а ты мне про утечки!

Так и я тебе про это, только там наверное close использовать нужно, хотя я хз, в примере используют close: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html но не суть важно.

Цитата JoeUser @
2 момент) Как мне RAII напомнит вызвать freeaddrinfo, тем более что выделял не я, а либа winsock2?

Ну а что ты когда пользуешься функциями/классами - ты по ним вообще доку не читаешь? Потом, зачем тебе юзать RAII - если у тебя не нужно освобождать ресурс руками? Я тебе про это и пишу.

Если ты начинаешь оборачивать ресурс в RAII Обертку - значит он не может сам по себе освободится, для этого RAII и нужен. Ты берешь функцию getaddrinfo, посмотри внимательно на последний ее параметр, он тебя нисколько не смущает? Меня он как раз и смутил:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);

addrs - у тебя неинициализированный указатель, а ты берешь его адрес и передаешь последним параметром в эту функцию, значит тут возможны два варианта - либо ты выхватишь Access Violation прямо внутри этой функции, либо она тебе вернет выделенную память(потому что если функция принимает указатель на указатель, и внутри работает с ним, не выделяя для него память - значит это AV, но обычно когда функция принимает указатель на указатель - практически всегда(ну кроме там исключительных случаев, например там работа с матрицами, двумерными массивами и т.д.) - это делается для того, чтоб выделить память и вернуть ее наружу, т.к. в С/С++ по умолчанию семантика значений, т.е. передай ты просто указатель - будет утечка), лезем в доку: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
И что мы видим?
Цитата

int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);

void freeaddrinfo(struct addrinfo *res);

const char *gai_strerror(int errcode);

Т.е. есть функция освобождения выделенного ресурса, соответственно ее и юзаем для освобождения памяти.
Когда в С работаешь - там нет классов и деструкторов, там все нужно руками освобождать, если не сказано обратное. Соответственно если есть функция, которая возвращает тебе ресурс, логично предположить что должна быть функция освобождения ресурса. В Си по другому не работают.
Допустим в apache есть функции для работы с ним, которым не нужно ресурсы руками освобождать, он сам их освобождает, там об этом прямо сказано, но там и типы соответствующие используются, а не голые указатели.

Ты когда у себя где то пишешь T* ptr = new T - ты потом память не освобождаешь что ли?

Цитата JoeUser @
Это скорее всего указатель на кишки libssh2, и они освободятся когда я вызову libssh2_exit();

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

Автор: OpenGL 13.07.20, 12:33
Цитата JoeUser @
Погоди, а в моем коде уже проблемы появились?

Я их описывал вообще-то. Сложное сопровождение кода, сложное понимание, и да - утечки ресурсов, т.к. если управление всеми ресурсами отдавать деструкторам, то от утечек это избавляет практически на 100%.

Автор: JoeUser 13.07.20, 14:10
Цитата Wound @
Так а какой тогда смысл в переменной fingerprint ?

Не знаю, и я об этом спрашивал на SO - тоже молчат. Если это обязательная последовательность диалога с SFTP, она должна быть в коде. А если нет - можно выбросить. Но я так и не нашел норм описание SFTP, т.к. RFC на него нету, а есть только draft-доки от IETF.

Цитата OpenGL @
управление всеми ресурсами отдавать деструкторам

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

Добавлено
Цитата Wound @
Ты когда у себя где то пишешь T* ptr = new T - ты потом память не освобождаешь что ли?

Всегда освобождаю. Но в Qt есть есть свой механизм, когда удалять не нужно, даже противопоказано. Пример в конструкторе:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ...
    QPushButton *B = new QPushButton("Акей");
    QVBoxLayout *L = new QVBoxLayout();
    L->addWidget(B);
    setLayout(L);


L->addWidget(B); - L запоминает потомка B, перед своим удалением - он его удалит
setLayout(L); - текущий виджет запоминает потомка L, перед своим удалением - он его удалит

Таким образом, мне в деструкторе делать ничо не нужно.

Автор: Wound 13.07.20, 14:28
Цитата JoeUser @
Не знаю, и я об этом спрашивал на SO - тоже молчат. Если это обязательная последовательность диалога с SFTP, она должна быть в коде. А если нет - можно выбросить. Но я так и не нашел норм описание SFTP, т.к. RFC на него нету, а есть только draft-доки от IETF.

:wacko: В конкретно данном случае - если ты выкинешь из кода fingerprint - это вообще ни на что не повлияет. Это ясно даже без доки.

Цитата JoeUser @
Ага и в деструкторах главное не забыть написать нужную функцию освобождения библиотечного ресурса. В плане с goto - та же проблема не забыть.

Я же пишу - это не тривиальная задача забыть в деструкторе написать освобождение. А вот с goto - это тривиальная задача. Обертку тебе нужно писать ровно 1 раз, потом раз ты в конструкторе выделяешь ресурс, значит если деструктор будет отсуствовать или он будет пустой - уже повод задуматься что ты вообще делаешь и нахрена ты такое пишешь.

Добавлено
Цитата JoeUser @
L->addWidget(B); - L запоминает потомка B, перед своим удалением - он его удалит
setLayout(L); - текущий виджет запоминает потомка L, перед своим удалением - он его удалит

Таким образом, мне в деструкторе делать ничо не нужно.

:bad: значит говно этот ваш QT, раз он смешивает семантику языка с логикой. В данном случае у тебя сырые указатели, я не понимаю - как он их удалит? Тут RAII и не пахнет. Разве что он как JAVA периодически подчищает ресурсы, но это уже сродни языку, а не фреймворку или что это вообще?

Я бы понял вот такой пример:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    QPushButton B = new QPushButton("Акей");
    QVBoxLayout L = new QVBoxLayout();
    L->addWidget(B);
    setLayout(L);


В таком виде - все ок, никаких неоднозначностей нет. А вот конкретно в том виде, в котором написал ты - у меня возникает много вопросов. Либо ты не верно понимаешь работу QT.

Добавлено
Вот к слову тут пишут что надо удалять ручками, либо юзать обертки: https://forum.qt.io/topic/80357/should-i-de...ocal-function/3

Добавлено
Да и это было бы довольно странно как ты написал. Кинь пруфы на документацию, что оно работает так, как ты пишешь. Потому что у меня закрадываются сомнения. Как оно понимает что я работаю с указателем и что в конкретном месте нужно освободить из под него память? Это анриал, это уже какая то Java получается, но даже там не знают когда надо освобождать память, поэтому собственно С++ еще и существует как язык.

Добавлено
А если я напишу:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int* pI = new int;

Оно мне тоже память почистит? Если нет, то в чем этот код отличается от:
Цитата JoeUser @
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    QPushButton *B = new QPushButton("Акей");
    QVBoxLayout *L = new QVBoxLayout();
    L->addWidget(B);
    setLayout(L);

???
Я больше склоняюсь к тому, что ты ошибся. Не может так быть.

Добавлено
Хотя если он там у себя внутри делает какой нибудь delete *this, может и будет работать(я уже два года на плюсах не писал, немного забывается), но всеравно это говноподход ИМХО, таким подходом всякие COM страдали на первых этапах, и то для них давно написаны RAII Обертки типа там ComPtr

Автор: OpenGL 13.07.20, 17:17
Цитата JoeUser @
Ага и в деструкторах главное не забыть написать нужную функцию освобождения библиотечного ресурса. В плане с goto - та же проблема не забыть.

Забыть написать освобождение ресурса во время написания RAII обёртки для этого ресурса это сильно :good:

Автор: Qraizer 13.07.20, 18:40
Цитата Wound @
А если я напишу:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int* pI = new int;

Оно мне тоже память почистит? Если нет, то в чем этот код отличается от:
Цитата JoeUser @
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    QPushButton *B = new QPushButton("Акей");
    QVBoxLayout *L = new QVBoxLayout();
    L->addWidget(B);
    setLayout(L);

???
Тем, что в случае Qt это не обязательно ошибка. Передавая ресурс в нутря Qt, ты передаёшь ему владение, согласно договорённости с Qt, тогда как присваивая поинтер на хип сырому указателю, никому ничего не передаёшь и по-прежнему сам отвечаешь за энтот поинтер. Сравни:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    std::shared_ptr<int> pI = new int;

Автор: applegame 13.07.20, 18:48
Как хорошо в D, в котором есть scope(exit). И не нужно ни goto ни бестолковых классов.

Автор: Wound 13.07.20, 18:53
Цитата Qraizer @
Тем, что в случае Qt это не обязательно ошибка. Передавая ресурс в нутря Qt, ты передаёшь ему владение, согласно договорённости с Qt, тогда как присваивая поинтер на хип сырому указателю, никому ничего не передаёшь и по-прежнему сам отвечаешь за энтот поинтер. Сравни:

Так я и говорю - смешение семантики языка с логикой выходит.

Когда ты пишешь вон как с std::shared_ptr - то в данном случае у тебя во первых разные типы данных с лева и с права.
Попробуй тогда уж написать по аналогии с QT:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    std::shared_ptr<int>* pI = new std::shared_ptr<int>();


Потому как с std::shared_ptr - сразу видно, что тут используется враппер, а когда ты пишешь:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    QPushButton *B = new QPushButton("Акей");

Тут из этого вообще никак не очевидно что ты пишешь враппер. Я во первых бы обернул это по привычке в std::unique_ptr, ну или на крайняк дальше по коду вызвал бы -> delete B;, а если дальше бы по коду оно упало у меня, то справедливо бы обматерил разрабов QT.
А в данном случае что происходит? Оно внутри делает delete *this?
Во первых это вообще не очевидно, во вторых - пописав годик вот на таком, ты по привычке везде будешь так писать, забыв вызвать delete, да и RAII тебе в таком случае нафиг не нужно, ты же привык что у тебя память сама освобождается. По мне так очень стремная практика. Даже в COM - и то юзаются врапперы.

Автор: JoeUser 13.07.20, 19:47
Цитата Wound @
Я больше склоняюсь к тому, что ты ошибся. Не может так быть.

Может. Ибо разрабы в QObject встроили звездолет, и для этого придумали свой дополнительный инструментарий - moc. Вот он-то и занимается тем, что на основе метаинформации начинает творить "чудеса".

И тем не менее, если написать new от класса-не потомка QObject - автоматического удаления не будет, ибо moc это не обрабатывает. Это по твоему примеру:

Цитата Wound @
А если я напишу:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int* pI = new int;



Добавлено
Цитата Wound @
Вот к слову тут пишут что надо удалять ручками, либо юзать обертки: https://forum.qt.io/topic/80357/should-i-de...ocal-function/3

Тут правильно пишут. Ибо если я напишу вот так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    QPushButton *B = new QPushButton("Акей");
    QVBoxLayout *L = new QVBoxLayout();
    // без следующих двух строчек
    // L->addWidget(B);
    // setLayout(L);

Будет утечка памяти, т.к. переменные B и L никому во владение не передаются. В таком случае, там же правильно предлагают использовать QScopedPointer. Но что объявление кнопки и ее неиспользование, что размещение QString в куче и отсутствие передачи указателя куда-то, кто удалит - это дичь.

Добавлено
Цитата OpenGL @
Забыть написать освобождение ресурса во время написания RAII обёртки для этого ресурса это сильно

Ну а чо? :lol: Я вон при cleanup сокета забыл сделать shutdown ему. По описалову - моя программа падать не будет, и система падать не будет. Будет только разница в поведении ранее созданного соединения, которое без shutdown будет по прежнему принимать TCP-пакеты, а в ответ молчать.

Автор: JoeUser 13.07.20, 20:21
Цитата Wound @
Кинь пруфы на документацию, что оно работает так, как ты пишешь.

Лень доки рыть. Кину на краткое описание механизма, там вроде понятно расписали - "Qt владение «объектами» (T * и const T *)"

Автор: Wound 13.07.20, 20:31
Цитата JoeUser @
Может. Ибо разрабы в QObject встроили звездолет, и для этого придумали свой дополнительный инструментарий - moc. Вот он-то и занимается тем, что на основе метаинформации начинает творить "чудеса".

И тем не менее, если написать new от класса-не потомка QObject - автоматического удаления не будет, ибо moc это не обрабатывает. Это по твоему примеру:

Поэтому и не знаешь что такое RAII и лепишь вские goto куда не попадя. А если кто то напишет у себя что то типа typedef unsigned int QUintPrimitive - то и утечку поиметь не долго.


Цитата JoeUser @
Тут правильно пишут. Ибо если я напишу вот так:

Вообще огонь. Зачем такую путаницу вводить - совершенно не понятно. По мне так проще QUnique_ptr/QShared_ptr/QWeak_ptr нарисовать и не вводить людей в заблуждение. Может это маркетинговый ход такой? Типа был нормальный С++сник, с годика два пописал на QT, и на С++ уже программировать не может, только на QT и остается писать, какой то свой изолированный от реальности мир этот ваш QT. Там поди и не все фишки новых стандартов нормально юзнешь?

Автор: JoeUser 13.07.20, 21:48
Цитата Wound @
Зачем такую путаницу вводить - совершенно не понятно.

Первый релиз Qt состоялся с 1996 году, какие нахрен shared_ptr тогда? Тогда код писали на ламповых мониторах! :lol:

Добавлено
Цитата Wound @
Там поди и не все фишки новых стандартов нормально юзнешь?

Все, что касается производных от QObject - с опаской. За использование исключений - расстрел сразу! Но тут я ни разу не парюсь, вон Rust тоже не парится.

Автор: Qraizer 13.07.20, 22:02
Вот поэтому я и выкинул Qt на головы. На кой хрен ещё один managed с++ по-над ещё одним дотНетом? Отказоустойчивости нет, обобщённости нет, населена велосипедами.

Добавлено
Цитата applegame @
Как хорошо в D, в котором есть scope(exit). И не нужно ни goto ни бестолковых классов.
Ой-ой-ой, а правильный скоп влеплять каждый раз никто не забывает, ага.

Автор: JoeUser 13.07.20, 22:18
Цитата Qraizer @
Вот поэтому я и выкинул Qt на головы.

Ну а я на свою голову освоил, надо было GUI под FreeBSD+кеды когда-то лепить. Но никогда не был этой либой доволен - любой продукт на ней истекает жЫром. Вот поэтому я и обратил свой взор на nana. 2 меговый хелоуворлд меня устраивает, пострипанный Qt-шны 27 меговый - бесит.

Автор: Qraizer 13.07.20, 22:20
На, вот. Отрыл-таки в Клубе энтот твой scope. Без Поиска, он не работает. Скопипасчу, не жалко.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    namespace ScopeD
    {
     
    class SuccessCase {} success;
    class FailureCase {} failure;
    class DoneCase    {} done;
     
    class scope
    {
      std::function<void(void)> fn1, fn2;
     
    public:
      template <typename T> scope(SuccessCase, T&& cl) try : fn1(std::move(cl)), fn2([](){})        {} catch (...) {       throw; }
      template <typename T> scope(FailureCase, T&& cl) try : fn1([](){}),        fn2(std::move(cl)) {} catch (...) { cl(); throw; }
      template <typename T> scope(DoneCase,    T&& cl) try : fn1(std::move(cl)), fn2(fn1)           {} catch (...) { cl(); throw; }
     
      ~scope() { if (std::uncaught_exceptions() != 0) fn2(); else fn1(); }
    };
     
    }
И использование последнего примера отсюда тоже
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    using namespace ScopeD;
     
      struct Foo
      {
        Foo(std::string s) { std::cout << s;   }
        ~Foo()             { std::cout << "1"; }
      };
     
     
      try
      {
        scope guard1(done,    [](){ std::cout << "2"; });
        scope guard2(success, [](){ std::cout << "3"; });
     
        Foo f("0");
     
        scope guard3(failure, [](){ std::cout << "4"; });
        throw std::runtime_error("runtime");
        scope guard4(done,    [](){ std::cout << "5"; });
        scope guard5(success, [](){ std::cout << "6"; });
        scope guard6(failure, [](){ std::cout << "7"; });
      }
      catch (const std::exception& e)
      {
        std::cout << "Exception: " << e.what();
      }
     
      std::cout << std::endl;

Автор: OpenGL 14.07.20, 03:11
Цитата Wound @
Зачем такую путаницу вводить - совершенно не понятно. По мне так проще QUnique_ptr/QShared_ptr/QWeak_ptr нарисовать и не вводить людей в заблуждение.

Это пошло ещё с самого зарождения Qt, тогда программировали по другому и всё, что тут писалось выше, ещё толком не было широко распространено. Ну и да, ты прав, по духу Qt это скорей "си с классами" библиотека, нежели плюсовая, и многие прожженые плюсовики её недолюбливают. Я сам ругаюсь на неё местами. И время от времени слышу, что "кутешник" это почти что ругательство в среде плюсовиков :D

Цитата Wound @
Там поди и не все фишки новых стандартов нормально юзнешь?

Да какой там новых. Там даже move конструкторов для тамошних смартпоинтеров не предусмотрено :D Так что если мне нужно сделать смартпоинтер, который вызывает deleteLater (это qt-way способ безопасно удалить QObject из любого потока), то я предпочитаю написать unique_ptr с кастомным deleter-ом, чем юзать QScopedPtr даже несмотря на то, что последний умеет удалять указатели на qobject посредством deleteLater из коробки.

Автор: applegame 14.07.20, 06:19
Цитата Qraizer @
На, вот. Отрыл-таки в Клубе энтот твой scope. Без Поиска, он не работает. Скопипасчу, не жалко.
Спасибо, что лишний раз подтвердил убогость и уродство плюсов. :lol:

Автор: Wound 14.07.20, 07:31
Цитата JoeUser @
Первый релиз Qt состоялся с 1996 году, какие нахрен shared_ptr тогда? Тогда код писали на ламповых мониторах!

И чего? Первый релиз плюсов состоялся в 1983 году, только где тот С++ и сегодняшний, это уже два совершенно разных языка. Переписать такие вещи раз плюнуть. Сначала пишешь нормальные RAII обертки, затем пишешь например дополнительный базовый тип, который лишен такого странного поведения, анонсируешь все это, помечаешь как deprecated старую логику. А потом потихонечко от нее избавляешься.
Это все реализуемо, было бы желание. А так мы имеем недоязык, недофреймворк, который подсаживает людей на себя, вангую после длительной, плотной работы на нем - ты уже не сможешь нормально писать на С++. Да ладно бы это был отдельный язык, куда не шло, а это же еще и юзает С++.


Цитата JoeUser @
Все, что касается производных от QObject - с опаской. За использование исключений - расстрел сразу! Но тут я ни разу не парюсь, вон Rust тоже не парится.

:wacko: А причем тут Rust ? Rust - это отдельный язык программирования со своим синтаксисом и остальным. А QT - это что такое ?

Автор: JoeUser 14.07.20, 07:36
Цитата Wound @
Переписать такие вещи раз плюнуть.

У Qt тож есть своя команда аналитиков - видимо что-то для посчитали не выгодным.

Цитата Wound @
было бы желание

Не-не-не - был бы профит в дополнительной прибыли, думаю и желание бы появилось.

Цитата Wound @
А причем тут Rust ?

При том, что нет там месту исключениям и их обработке, они так решили.

Автор: Wound 14.07.20, 07:38
Цитата OpenGL @
Это пошло ещё с самого зарождения Qt, тогда программировали по другому и всё, что тут писалось выше, ещё толком не было широко распространено. Ну и да, ты прав, по духу Qt это скорей "си с классами" библиотека, нежели плюсовая, и многие прожженые плюсовики её недолюбливают. Я сам ругаюсь на неё местами. И время от времени слышу, что "кутешник" это почти что ругательство в среде плюсовиков

Ну я пару раз пробовал за него садиться, и все разы я не понимал как на нем что то сделать нормальное, плевался и бросал. Но вот конкретно до таких вещей я не доходил, так бы и не садился за него вовсе.

Цитата OpenGL @
Да какой там новых. Там даже move конструкторов для тамошних смартпоинтеров не предусмотрено :D Так что если мне нужно сделать смартпоинтер, который вызывает deleteLater (это qt-way способ безопасно удалить QObject из любого потока), то я предпочитаю написать unique_ptr с кастомным deleter-ом, чем юзать QScopedPtr даже несмотря на то, что последний умеет удалять указатели на qobject посредством deleteLater из коробки.

Мда... Так они бы уже не парились и написали для него свой ЯП, чем переписывать и уродовать С++ :-?

Добавлено
Цитата JoeUser @
У Qt тож есть своя команда аналитиков - видимо что-то для посчитали не выгодным.

Я думаю это маркетинговый ход такой. Сначало подсадить людей на QT, потом спустя время человек кроме QT в тех же плюсах уже профнепригоден, вуаля, когда человек поймет это - для него уже станет поздно. И вот развивают свою экосистему QT.

Цитата JoeUser @
Не-не-не - был бы профит в дополнительной прибыли, думаю и желание бы появилось.

Я думаю если бы QT не занимался вот этим вот, а вел себя как некий фреймворк использующий С++ по правилам С++, а не изменяющий его правила, то у него была бы в разы больше аудитория.

Цитата JoeUser @
При том, что нет там месту исключениям и их обработке, они так решили.

И чего? Ты не путай волчий хрен и колбасу. Rust - это язык программирования. В Ассемблере тоже нет исключений, и чего? В Prolog тоже нет исключений. Дело то не в том есть исключения или нет, дело в том - что они есть в языке, а фреймворк тебе их запрещает юзать, вернее тебе разрабы запрещают юзать. QT - это язык программирования? ЕМНИП он компилятор С++ юзает. Это обычный фреймворк, только я не слышал больше фреймворков, которые берут изменяют логику работы языка который используют.

Автор: JoeUser 14.07.20, 08:56
Цитата Wound @
а фреймворк тебе их запрещает юзать, вернее тебе разрабы запрещают юзать.

Ну там вообще немного не так. Запрет касается только одной ситуации, ЕМНИП, нельзя генерировать исключения в обработчике событий Qt. Что-то типа нельзя:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    try {
      connect(thread, &MyThread::started, this, &Element::count);  // слот count() может бросать исключения
    }
    catch(const X& e) {}


Но если тебе это нужно прямо очень, что кушать не можешь, можно обойти так:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class BadCounter {
        Q_OBJECT
      public slots:
        void count() { throw std::runtime_error("хана, исключение!"); }
    };
     
    class BadCounterAdaptor {
      Q_OBJECT
        BadCounter* counter;
      public:
        BadCounterAdaptor(BadCounter* iCounter):counter(iCounter) {}
      public slots:
        void count() {
          try {
            counter->count();
          } catch (const std::runtime_error& e) {
            std::cerr << e.what() << std::endl;
          } cstch (...) {
            std::cerr << "Случилось страшное!" << std::endl;
          }
        }
    };
     
    int main() {
      BadCounter Counter;
      BadCounterAdaptor Adaptor(&Counter);
      QThread* Thread = new QThread();
      connect(Thread,&QThread::started,&Adaptor,&BadCounterAdaptor::count);
      Thread.start();
      ...
      delete Thread;
    }

В остальных случаях - вполне можно пользоваться исключениями нативно. Поэтому тут скорее не запрет на все - а предложение "мол мы вот так либу запроектировали, не будьте калдырями - поддерживайте нашу идеологию, ну или валите на MFC нафик!" :)

Добавлено
Цитата Wound @
ело в том - что они есть в языке, а фреймворк тебе их запрещает юзать

Мы живем в несправедливом мире! Часто за все нужно платить :'(

Автор: Wound 14.07.20, 09:04
Цитата JoeUser @
"мол мы вот так либу запроектировали, не будьте калдырями - поддерживайте нашу идеологию, ну или валите на MFC нафик!"

Ааа, QTшники живут еще в 2008 году наверное :D MFC - уже много лет как умер, он уже не разрабатывается, и не поддерживается даже мелкомягкими. А че по поводу нового стандарта они говорят? Пишите вот так как мы напроектировали или валите на свой С++17 или какой там уже? С++20?
Они сами себе яму то и копают. Когда профит от языка превысит профит от QT его просто не будут использовать. Я не знаю какой сейчас профит от QT? Единственное что я вижу - это GUI под Linux, и то, это потому что я не знаю какие там фреймворки для Linux'ов есть сейчас.
Потому что в остальном его обходит тот же .net core/C++, для мобильных платформ Java со всякими Unity.

Добавлено
Цитата JoeUser @
Мы живем в несправедливом мире! Часто за все нужно платить

А профит от него какой? С++ с новый стандартом уже становится кросплатформенным в плане работы с системными API(потоки там всякие, файловая система, и т.д.), туда добавляют новые примочки каждый год.

Автор: JoeUser 14.07.20, 09:43
Цитата Wound @
А профит от него какой?

Ну начинался он GUI конечно. К тому времени у него основные конкуренты были GTK и wxWindows (ныне wxWidgets). Сейчас Qt сильно разросся во что только можно. Под спойлером либы, по названию поймешь, чем щя рулит Qt:

Скрытый текст
Qt5Bluetooth.dll
Qt5Charts.dll
Qt5Concurrent.dll
Qt5Core.dll
Qt5DBus.dll
Qt5Designer.dll
Qt5DesignerComponents.dll
Qt5Gamepad.dll
Qt5Gui.dll
Qt5Help.dll
Qt5Location.dll
Qt5Multimedia.dll
Qt5MultimediaQuick.dll
Qt5MultimediaWidgets.dll
Qt5Network.dll
Qt5NetworkAuth.dll
Qt5Nfc.dll
Qt5OpenGL.dll
Qt5Positioning.dll
Qt5PositioningQuick.dll
Qt5PrintSupport.dll
Qt5Qml.dll
Qt5QmlModels.dll
Qt5QmlWorkerScript.dll
Qt5Quick.dll
Qt5QuickControls2.dll
Qt5QuickParticles.dll
Qt5QuickShapes.dll
Qt5QuickTemplates2.dll
Qt5QuickTest.dll
Qt5QuickWidgets.dll
Qt5RemoteObjects.dll
Qt5Script.dll
Qt5ScriptTools.dll
Qt5Scxml.dll
Qt5Sensors.dll
Qt5SerialBus.dll
Qt5SerialPort.dll
Qt5Sql.dll
Qt5Svg.dll
Qt5Test.dll
Qt5TextToSpeech.dll
Qt5WebChannel.dll
Qt5WebSockets.dll
Qt5Widgets.dll
Qt5WinExtras.dll
Qt5Xml.dll
Qt5XmlPatterns.dll
Qt53DAnimation.dll
Qt53DCore.dll
Qt53DExtras.dll
Qt53DInput.dll
Qt53DLogic.dll
Qt53DQuick.dll
Qt53DQuickAnimation.dll
Qt53DQuickExtras.dll
Qt53DQuickInput.dll
Qt53DQuickRender.dll
Qt53DQuickScene2D.dll
Qt53DRender.dll

Автор: Wound 14.07.20, 10:12
Цитата JoeUser @
Сейчас Qt сильно разросся во что только можно. Под спойлером либы, по названию поймешь, чем щя рулит Qt:

Ну я тут вижу только GUI. Графику на нем я бы не стал писать, есть куча фреймворков и движков куда круче с полной поддержкой языка. Остальное либо мелочи, либо устаревшее что то, либо просто как с боку бантик.
Для каких то небольших проектов возможно и сойдет, но небольшие проекты сейчас все больше разрабатывают для мобильных платформ, они там более востребованы. :-?

Автор: D_KEY 14.07.20, 11:31
Цитата applegame @
Цитата Qraizer @
На, вот. Отрыл-таки в Клубе энтот твой scope. Без Поиска, он не работает. Скопипасчу, не жалко.
Спасибо, что лишний раз подтвердил убогость и уродство плюсов. :lol:

И в чем же такая принципиальная разница между:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    scope(exit) foo(1, 2, 3);


и

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    scope guard(exit, []{foo(1, 2, 3);});


?

Автор: Qraizer 14.07.20, 11:35
Цитата applegame @
Спасибо, что лишний раз подтвердил убогость и уродство плюсов.
Не за что. Я там так и говорил, что scope – это C-подобное убожество, и лучше уж
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class Guard
    {
      std::function<void(void)> fn;         // функция очистки
     
    public:
      template <typename T> explicit Guard(T&& closure): fn(std::move(closure)) {}
      ~Guard() { fn(); }
    };

с последующими там
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      HANDLE sameApp = CreateMutex(NULL, TRUE, uniqName.c_str());
     
      if (sameApp == NULL)
        return static_cast<void>(logMsg("CreateMutex() failed", GetLastError()));
     
      Guard sameAppGuarded([=]() { CloseHandle(sameApp); });
     
      if (GetLastError() == ERROR_ALREADY_EXISTS)
        if (WaitForSingleObject(sameApp, INFINITE) != WAIT_OBJECT_0)
          return static_cast<void>(logMsg("Recursive run detected for " + name.filename().string(), GetLastError()));
      Guard sameAppLocked([=]() { ReleaseMutex(sameApp); });
     
      {
        SECURITY_ATTRIBUTES attr = { sizeof(attr), NULL, TRUE };
        HANDLE hIn  = CreateFile((newName.parent_path() / "stdin.txt").string().c_str(),
                                 FILE_GENERIC_READ, FILE_SHARE_DELETE,&attr, CREATE_ALWAYS,
                                 FILE_ATTRIBUTE_NORMAL, NULL);
        Guard hInGuarded ([=]() { CloseHandle(hIn);  });
    /* ... */
      }
что тоже костыль, но уже гораздо меньший, ибо является GNUсным аналогом его идиомы. К тому же без досадных недостатков последнего, что даже в C вызывали сложности.

Автор: D_KEY 14.07.20, 11:38
Цитата JoeUser @
Сейчас Qt сильно разросся во что только можно.

Лично я бы qt не стал использовать ни для чего, кроме GUI. Да и GUI только из-за того, что альтернатив полноценных для C++ нет.

Это фреймворк со своими правилами, которые уже давно очень сильно разошлись с правилами, принятыми в C++.

Автор: Qraizer 14.07.20, 11:43
Цитата D_KEY @
Да и GUI только из-за того, что альтернатив полноценных для C++ нет.
Поговаривают, одно время там обсуждалось что-то типа std::gui.
Цитата D_KEY @
Это фреймворк со своими правилами, которые уже давно очень сильно разошлись с правилами, принятыми в C++.
Это-то и настораживает. Когда-то точно также VCL разошёлся с WinAPI

Автор: Wound 14.07.20, 12:04
Цитата Qraizer @
Поговаривают, одно время там обсуждалось что-то типа std::gui.

Ну вот если это введут, то это и будет killer-фичей, которая убьет QT.

Добавлено
Цитата Qraizer @
Это-то и настораживает. Когда-то точно также VCL разошёлся с WinAPI

Но VCL вывозил за счет своей простоты. На нем за денек можно было налабать какое то сурьезное приложение, которое там работает со звуком, БД, сетью еще чета там. Причем не имея в этом огромного опыта. Буквально сходу сел и пиши. В то время когда такое же приложение написать на том же MFC - уже было проблематично в указанные сроки, а на WinAPI и подавно - геморой.
А в QT - поди разберись с ходу, я как то года два-три назад скачал QT Creator все поставил, сожрало места на диске дохрена, запустил, чета там потыкал, думал щас как в VCL, ага, фигвам! Потыкал потыкал, оно у меня что то там не собралось, я на это дело плюнул и забил.

Автор: D_KEY 14.07.20, 12:12
Цитата Qraizer @
Поговаривают, одно время там обсуждалось что-то типа std::gui.

Честно говоря, я против. Это довольно динамичная и не достаточно фундаментальная сфера, чтоб пихать ее в std.
Возможно, стоит добавить в C++ какую-то расширенную стандартную библиотеку и туда такие вещи выносить. Например, что-то вроде asio туда просится(а вот в std таки не стоит).

Автор: Wound 14.07.20, 12:20
Цитата D_KEY @
Честно говоря, я против. Это довольно динамичная и не достаточно фундаментальная сфера, чтоб пихать ее в std.
Возможно, стоит добавить в C++ какую-то расширенную стандартную библиотеку и туда такие вещи выносить. Например, что-то вроде asio туда просится(а вот в std таки не стоит).

Я бы хотел чтоб в С++ внедрили подключаемые публичные репы, как это сделано в том же .NET - nuget пакеты, очень удобная вещь.
Во первых отпадает необходимость тянуть все попало в язык, типа той же gui, во вторых не нужно замарачиваться где искать, прям из среды разработки зашел - указал репу, и все, либо руками в проект прописал и собирай.
Это очень удобно.

Добавлено
Просто в комитете по стандартизации засиделись заскорузлые ретрограды, у которых в жопе ностальгия по 80-90-м играет. Поэтому они не делают смелых движений, и по крупице что то там вводят, что уже порой даже отжило себя.

Автор: Qraizer 14.07.20, 16:22
Цитата D_KEY @
Честно говоря, я против. Это довольно динамичная и не достаточно фундаментальная сфера, чтоб пихать ее в std.
За std::filesystem то же говорили. И за многопоточность. Ничего, сейчас живёт в неком виде, и даже вполне себе сносно выглядит. Другое дело, что я как-то с трудом себе представляю такую штуку стандартизированной, слишком уж комплексная. Это ж не просто окошки порисовать, ещё ж интерактивность какая-никакая нужна.
Цитата Wound @
Я бы хотел чтоб в С++ внедрили подключаемые публичные репы, как это сделано в том же .NET - nuget пакеты, очень удобная вещь.
Та кто ж против-то. В конце концов в любой инфраструктуре уже есть подобное. Пусть и разрозненное в целом, но в частности вполне себе централизованное. Где-то это дебиановый репозиторий, где-то убунтовый, где-то макрософтовский сторадж. Но ведь это есть там, где есть конкретный хозяин. У того же .NETа он есть, у Дебиана есть, а где он у Плюсов? У него нет конкретного хозяина. Та и в принципе быть не может, Плюсы позиционируются как единые для всех и каждого, не исключая встроенные системы на простеньких микроконтроллерах. Ну вот есть какой-то там буст. Э-э-э...

Автор: Wound 14.07.20, 16:35
Цитата Qraizer @
Та кто ж против-то. В конце концов в любой инфраструктуре уже есть подобное. Пусть и разрозненное в целом, но в частности вполне себе централизованное. Где-то это дебиановый репозиторий, где-то убунтовый, где-то макрософтовский сторадж. Но ведь это есть там, где есть конкретный хозяин. У того же .NETа он есть, у Дебиана есть, а где он у Плюсов? У него нет конкретного хозяина. Та и в принципе быть не может, Плюсы позиционируются как единые для всех и каждого, не исключая встроенные системы на простеньких микроконтроллерах. Ну вот есть какой-то там буст. Э-э-э...

Так кто то же занимается разработкой нового стандарта, вот пусть сядут подумают, не придумают, на крайняк можно попросить мелкомягких, те с радостью думаю предоставят площадку для такого, тем более что технология уже давно обкатана. Было бы желание, как говорится. Стандарт то откуда то скачать можно, кто то же его писал, хозяина нет, а стандарт разрабатывается. Пусть на те же донаты площадку поднимут. Я не думаю что это какая то проблема.

Автор: D_KEY 14.07.20, 16:44
Цитата Wound @
Я бы хотел чтоб в С++ внедрили подключаемые публичные репы

Да, я тож за.

Добавлено
Цитата Qraizer @
Другое дело, что я как-то с трудом себе представляю такую штуку стандартизированной, слишком уж комплексная. Это ж не просто окошки порисовать, ещё ж интерактивность какая-никакая нужна.

О том и речь.

Автор: JoeUser 14.07.20, 19:33
Цитата D_KEY @
Да, я тож за.

Я тоже за :lol:

Как-то собирал проект на Расте, чисто ради прикола (ел пельмени со сметаной в это время, домашние - с телятиной!). Так тамошний Cargo четко так, по-пацански, вытащил либы НУЖНЫХ ВЕРСИЙ, собрал их и потом уже прилинковал к откомпиляченным файлам проекта. Я чуть самый сочный пельмень не уронил!!! Ну очень мне эта кухня понравилась!!!

Но и в плюсах тож есть неизвестные герои, не побоюсь - без кавычек! Я как-то уже не раз заикался тут о проекте mxe.cc. ИМХО - проблески рационального подхода! Чел (или группа, не в курсе) замутил(и) систему кросс-компиляции Линупс->Уиндовс. И не абы какую, а с поддержкой определенного репозитария. Меня вот лично удивил порт DBUS на винду тамака реализованный :blink: Знаю - у пипла двоякое мнение на счет DBUS, пофик - важен сам факт. Да и сам факт существования этого проекта.

Это я к чему ... Есть проекты, которые могут стать чем-то большим, но увы - они зиждятся только на интузязизьме. Вот Киля хочет централизованную базу пэкеджей для С++ (либ, которые собираются без гемора и вскрытия вен под различные цели), ну как в Rust, Activestate Perl, возможно и в питоне, да - в PHP ... и я хочу, и все хотят - а кому этим заняться???

Скрытый текст
Вон в соседней теме "Куда двигаться дальше?" наши коллеги копья ломают о спинной мозг! А первый креатив - он, блин, на поверхности ... Замутить на базе исходников билд-сервер для плюсов!!! Но не просто и тупо. А взять пример с версионности растовского Cargo. Да, получится нефигенный граф зависимостей либ и их версий. Но и профит будет жэстачайшы (С)(бел.мова бел.лiдэра), имхо. Сюда бы финансистов и финансовых аналитиков привлечь - катались бы все как масло по шоколаду! :lol:

Автор: D_KEY 14.07.20, 23:33
Для C++ довольно сильно распространен conan.

Автор: JoeUser 16.07.20, 04:17
Цитата D_KEY @
Для C++ довольно сильно распространен conan.

Проект интересный, но качество неочень. К примеру, из доступных предкомпилированных версий OpenSSL для винды, для GCC вааще ничего нет, только под Линупс. Только рецепты.

Автор: applegame 16.07.20, 11:34
Цитата D_KEY @
И в чем же такая принципиальная разница между:
В оверхеде, вестимо. Запихивание лямбды в std::function будет сопровождаться аллокациями в куче, даже если лямбда без замыканий. scope(exit) лишен этого недостатка. Хотя есть ненулевая вероятность, что шибко умный компилятор сумеет соптимизировать это дело, но я сомневаюсь, надо пробовать.
Ну и многословнее оно на плюсах, имена нужно гвардам давать, да еще и не повторяющиеся. Костыль, короче.

Автор: Qraizer 16.07.20, 11:49
Зависит от реализации. std::basic_string<> тоже далеко не во всех случаях использует хип. Простые – а замыканиями обычно таковые и являются, ибо помимо самого замыкаемого объекта других состояний не хранят – будут себе спокойно лежать на стеке.

Добавлено
Цитата applegame @
Костыль, короче.
Зато с ручкой, в отличие от Dшного.

Автор: D_KEY 16.07.20, 12:16
Цитата applegame @
В оверхеде, вестимо. Запихивание лямбды в std::function будет сопровождаться аллокациями в куче, даже если лямбда без замыканий.

Я помню был какой-то похожий спор с твоим участием (кажется в тематике), где в итоге обнаружилось, что std::function не уступает Dшным замыканиям :)

Там можно сделать и полностью шаблонный класс, без std::function, а deduction guides позволят не писать лишний раз ни типы, ни обертки в духе make_*.

Добавлено
Цитата applegame @
Ну и многословнее оно на плюсах, имена нужно гвардам давать, да еще и не повторяющиеся.

Не думаю, что это проблемой будет реальной.

Цитата
Костыль, короче.

Тут, конечно, от определения "костыля" зависит, но если понимать под костылем быстрое плохое решение вместо правильного, но более трудоемкого, то разницы с D никакой.

Автор: applegame 16.07.20, 13:22
Цитата D_KEY @
Я помню был какой-то похожий спор с твоим участием (кажется в тематике), где в итоге обнаружилось, что std::function не уступает Dшным замыканиям
Да, я тоже что-то такое помню, но также помню, что там был некий вырожденный случай, когда весь код в одном файле и компилятору дрступен весь код сразу. Но если вынести код с std::function в другие единицы трансляции, то аллокации в принципе неизбежны.

Автор: D_KEY 16.07.20, 13:26
Цитата applegame @
Но если вынести код с std::function в другие единицы трансляции, то аллокации в принципе неизбежны.

А lto не помогает в таких случаях разве?

Автор: Wound 16.07.20, 13:26
Оно?

https://en.cppreference.com/w/cpp/experimental/scope_exit
https://en.cppreference.com/w/cpp/experimental/scope_fail
https://en.cppreference.com/w/cpp/experimental/scope_success

Хотя как по мне - еще один синтаксический сахар, и не более. можно заменить тем же std::unique_ptr с кастомным делитером.

Автор: applegame 16.07.20, 13:29
Цитата D_KEY @
Не думаю, что это проблемой будет реальной.
Костыли далеко не всегда являются реальной проблемой. Но это все равно костыли. Например, std::enable_if - это жутчайший и уродский костыль, но наверняка Qraizer так не считает. Да и реальной проблемой он не является :D

Добавлено
Цитата D_KEY @
А lto не помогает в таких случаях разве?
lto умеет менять код сгенеренный компилятором?

Автор: D_KEY 16.07.20, 13:40
Цитата applegame @
Костыли далеко не всегда являются реальной проблемой.

Про костыли я тебе выше написал. Это не костыль. Вернее, если это костыль, то и в D - тоже костыль.

Цитата
Например, std::enable_if - это жутчайший и уродский костыль

Вот тут согласен.
Ну, кстати, if constexpr почти все решает :)

Автор: Qraizer 16.07.20, 15:40
Цитата applegame @
Например, std::enable_if - это жутчайший и уродский костыль, но наверняка Qraizer так не считает.
Я его не использую. Не нужно было просто. Но это и не костыль, это составная часть интерфейса std в части поддержки парадигмы метапограммирования, которая помимо этого средства включает в себя ещё и большое количество других. На основе этой парадигмы ты можешь учить компилятор работать с любыми сущностями на основе их свойств и характеристик, а не только пользоваться предопределёнными и стандартизированными наборами для ограниченного числа сущностей и определёнными в Стандарте. Или ты шейдеры для GPU тоже называешь костылями, тогда как истинно правильными и единственно правоверными считаешь mutlitexturing, еnvironment lighting, mipmapping, T&L и ещё с десяток предопределений?

Добавлено
Цитата D_KEY @
Вернее, если это костыль, то и в D - тоже костыль.
Костыль, костыль. И то, и это тоже. Негоже управлять ресурсами вручную. Негоже строить безотказность кода на условных конструкциях, не имеющих отношения к инвариантам. За которые и непонятно кто ещё отвечает. И то, и другое, и третье имеет место в scope, как и где его не реализуй.

Автор: applegame 17.07.20, 08:48
использовать scope(exit) в качестве иммитации RAII действительно глупо. Но бывают случаи где сия конструкция вполне уместна.
Например, я писал обертку для постгресовской родной либы libpq. Она написана на сяшке (как и весь постгрескуль). Там память захватывается самой либой, а освобождается уже клинтским кодом путем вызова соответствующей функции. Это освобождение в моей обертке фигурировало буквально внутри одной функции. Я использовал там scope(exit) так как посчитал абсолютно бесполезным городить отдельный класс вместо одной сраной строчки: scope(exit) PQfree(res).

Кроме того scope(exit) можно применять не только для освобождения ресурсов. Я пихал в него временный отладочный код вроде изменения счетчиков и всяких логов.

Автор: Wound 17.07.20, 10:31
Цитата applegame @
Она написана на сяшке (как и весь постгрескуль). Там память захватывается самой либой, а освобождается уже клинтским кодом путем вызова соответствующей функции. Это освобождение в моей обертке фигурировало буквально внутри одной функции. Я использовал там scope(exit) так как посчитал абсолютно бесполезным городить отдельный класс вместо одной сраной строчки: scope(exit) PQfree(res).

Ты просто видимо из за отсутствия возможностей в D неверно спроектировал работу с библиотекой с точки зрения С++. Поэтому тебе и проще было сделать так. В С++ такой подход - это не проще, это будет тупо костыль.
Вот про эту либу ты говоришь?
https://postgrespro.ru/docs/postgresql/9.6/libpq-connect

Во первых тут нет никакой функции PQfree. Есть:
Цитата

PQfinish

Закрывает соединение с сервером. Также освобождает память, используемую объектом PGconn.

void PQfinish(PGconn *conn);


Во вторых - судя по документации, туда как раз RAII класс и просится, не хочешь городить класс, пиши auto dbCon = std::unique_ptr<PGconn>(PQconnectdb(psql_connect_info), [](PGconn* pCon){PQFree(pCon)}) и все, больше не нужно задумываться ни о чем. Так что ИМХО, пример так себе. Вот в своей либе, ты передаешь в PQFree(res) - а откуда ты его - res - получил? Вызвал коннект? Так значит уже не ради одной строчки класс городить, а ради двух получается? Или как?

Цитата applegame @
Кроме того scope(exit) можно применять не только для освобождения ресурсов. Я пихал в него временный отладочный код вроде изменения счетчиков и всяких логов.

Это очень просто делается классом-оберткой. В D возможно эта функция полезна, там же вроде GC? А в С++ она просто нахер никому не нужна. Ну не нужна она, если нужно что то подобное - это делается 1 раз за 2 минуты на коленке, и используется везде где нужно.

Это как знаешь, сказать в C#/Java есть ах**наая плюшка finally, а в С++ ее нет, бе бе бе. Вы мудохайтесь там теперь и живите с этим, а я finally юзаю и доволен. Ну как то вот так это выглядит :D

Ну и к слову, встречал что если класс в D создавать через new, то порядок вызовов с этим scope(exit) - уже не такой однозначный, а все из за GC, так что даже эта "плюшка" может запросто поломать программу. Ей очень осторожно нужно пользоваться. А значит уже и ценность ее, какую ты в нее вкладываешь, сомнительная.
Вот собственно тут об этой особенности и пишут: https://forum.dlang.org/thread/kjkdfcnpgdeg...forum.dlang.org
Т.е. как минимум один уже выхватил сигфолт в своей программе из за этого scope(exit)

Автор: applegame 17.07.20, 13:18
Цитата Wound @
Ты просто видимо из за отсутствия возможностей в D неверно спроектировал работу с библиотекой с точки зрения С++. Поэтому тебе и проще было сделать так.
Нет, ты ошибаешься. В D можно написать также как и в C++, используя вместо классов структуры, без использования GC и с точно таким же автоматическим вызовом дуструктора при выходе из скоупа.
Цитата Wound @
Во первых тут нет никакой функции PQfree.
Да, давно писал, подзабыл, правильно PQClear.
Цитата Wound @
Во вторых - судя по документации, туда как раз RAII класс и просится, не хочешь городить класс, пиши auto
Цитата
dbCon = std::unique_ptr<PGconn>(PQconnectdb(psql_connect_info), [](PGconn* pCon){PQFree(pCon)})
и все, больше не нужно задумываться ни о чем. Так что ИМХО, пример так себе. Вот в своей либе, ты передаешь в PQFree(res) - а откуда ты его - res - получил? Вызвал коннект? Так значит уже не ради одной строчки класс городить, а ради двух получается? Или как?
Нет, PQFinish я как раз вызываю в деструкторе класса Connection. А эти коннекшены я держу в пуле, все по взрослому, тут RAII не нужно, как и scope(exit) или unique_ptr.

Ну а вообще, когда я был молодым и глупым и сильно фанател по плюсам, вот это вот:
Цитата Wound @
auto dbCon = std::unique_ptr<PGconn>(PQconnectdb(psql_connect_info), [](PGconn* pCon){PQFree(pCon)})

Мне казалось класной штукой. Но как только я увидел и попробовал D, Ruby, Haskell, Erlang, Elixir, Python, и прочее, я осознал, что это просто нечитаемое говно. Да ты сам, помню, ругал плюсы за уродливое метапрограммирование. Сравни:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res = PQExec(...);
    scope(exit) PQClear(res);

Чисто и любому, деже не умеющему в D, ясно что происходит. Можно конечно написать RAII обертку, но я давно переболел ООП головного мозга и перестал писать классы/структуры на малейший чих.
Цитата Wound @
Ну и к слову, встречал что если класс в D создавать через new, то порядок вызовов с этим scope(exit) - уже не такой однозначный, а все из за GC, так что даже эта "плюшка" может запросто поломать программу. Ей очень осторожно нужно пользоваться. А значит уже и ценность ее, какую ты в нее вкладываешь, сомнительная.
Не понимаю о чем ты. Что именно ты слышал?

Автор: Wound 17.07.20, 13:40
Цитата applegame @
Нет, ты ошибаешься. В D можно написать также как и в C++ используя не классы, а структуры, без использования GC и с точно таким же автоматическим вызовом дуструктора при выходе из скоупа.

Ну не удивительно, я же не пишу на D, поэтому я предположил. А раз есть такая возможность - значит, выходит просто ты так спроектировал, что тебе проще вызвать scope(exit), вместо нормальной, красивой, надежной RAII обертки.

Цитата applegame @
Нет, PQFinish я как раз вызываю в деструкторе класса Connection. А эти коннекшены я держу в пуле, все по взрослому, тут RAII не нужно, как и scope(exit) или unique_ptr.

Ну так я же не знаю что ты там использовал, PQfree я не нашел, а под твое описание подпадает только PQFinish.
PQClear же работает в связке с PQExec, опять же это все можно обернуть в обертку. Как сделано у тебя, правильно не правильно, не суть важно. Важно то, что тот контекст в котором ты говоришь, что якобы удобно писать scope(exit) вот тут - это удобно при твоей архитектуре приложения и удобно в том языке, в котором ты пишешь. Т.е. это не фича, что без нее вот тут будет плохо, это просто ты так написал свою программу. Я например напишу так, что в scope(exit) не будет никакого смысла, другой напишет так, что без goto не обойдешься.

Цитата applegame @
Ну а вообще, когда я был молодым и глупым и сильно фанател по плюсам, вот это вот:
Цитата Wound @ Сегодня, 13:31
auto dbCon = std::unique_ptr<PGconn>(PQconnectdb(psql_connect_info), [](PGconn* pCon){PQFree(pCon)})

Мне казалось класной штукой. Но как только я увидел и попробовал D, Ruby, Haskell, Erlang, Elixir, Python, и прочее, я осознал, что это просто нечитаемое говно. Да ты сам, помню, ругал плюсы за уродливое метапрограммирование.

Таки да, я же не спорю что в С++ вырвиглазные шаблоны, в которых без поллитрyхи не разберешься. Я с этим полностью согласен. Но речь то шла не о вырвиглазных шаблонах.

Цитата applegame @
Сравни:
CollapsedWrap disabledLine numbers off

auto res = PQExec(...);
scope(exit) PQClear(res);


Чисто и любому, деже не умеющему в D, ясно что происходит. Можно конечно написать RAII структурку, но я давно переболел ООП головного мозга и перестал писать классы/структуры на малейший чих.

Вся фишка в том, что возможно это чисто и ясно в D, но в С++ такие махинации чреваты багами, поэтому в С++ приходится писать в RAII стиле не потому что это как то круто, модно, молодежно, а потому чтоб сократить число потенциальных багов. Да и удобнее банально этим будет пользоваться, чем голыми указателями и Си функциями. Это источник многих ошибок, вот чтоб этого избежать и пишут RAII обертки, тем более что язык это поддерживает искаропки. Так почему бы и не воспользоваться этим преимуществом.

Добавлено
Цитата applegame @
Не понимаю о чем ты. Что именно ты слышал?

Я же ссылку привел выше, ок продублирую еще раз:
https://forum.dlang.org/thread/kjkdfcnpgdeg...forum.dlang.org

Вот тут чувак выхватил сигфолт благодаря GC и scope(exit).

Добавлено
Я вот допустим писал какую то хрень, связанную с вычиткой ACL в винде и ее последующим парсингом, жуткая хрень, пятиэтажные вложенные структуры, везде одни сплошные указатели, все освобождается какими то специфическими функциями, куча всякой дряни. Мне реально было проще взять все эти указатели и обернуть в unique_ptr с кастомным делитером, и вообще не нужно парится по поводу утечек, при любых раскладах. в итоге мне вообще фиолетово было на какие то там ошибки, которые могут произойти(нет они фиксировались и в логе и юзеру), просто сам ресурс - уже полностью контролируется компилятором, т.е. утечки будут только если в компиляторе баг. А это надежнее, чем писать во многих разных местах руками освобождение ресурса, потому как ты можешь не то освободить, забыть написать, не там освободить. Ручное управление ресурсом - всегда хуже автоматического, потому как является источником многих косяков и багов.

Автор: Qraizer 17.07.20, 14:38
Цитата applegame @
Но как только я увидел и попробовал D, Ruby, Haskell, Erlang, Elixir, Python, и прочее, я осознал, что это просто нечитаемое говно.
Чё-то я в этом списке Delphi не вижу. Непорядок?

Добавлено
Цитата applegame @
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res = PQExec(...);
    scope(exit) PQClear(res);

Чисто и любому, деже не умеющему в D, ясно что происходит. Можно конечно написать RAII обертку, но я давно переболел ООП головного мозга и перестал писать классы/структуры на малейший чих.
Это прекрасно, пока не встретишь
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res1 = PQExec(...);
    scope(exit) PQClear(res1);
    auto res2 = PQExec(...);
    scope(exit) PQClear(res1);
    auto res3 = PQExec(...);
    scope(exit) PQClear(res1);

Автор: D_KEY 17.07.20, 14:59
Цитата applegame @
Мне казалось класной штукой. Но как только я увидел и попробовал D, Ruby, Haskell, Erlang, Elixir, Python, и прочее, я осознал, что это просто нечитаемое говно.

Я тоже пробовал много чего, в том числе и то, что указал ты (кроме elixir, да и D мало слишком).
Я не считаю C++ лучшим или что-то такое, но вот RAII очень крутая (и при этом простая) концепция.

Как раз всякие менеджеры контекста в питоне, которые я активно использую, таки костыли. Но это уже лучше всяких finally и defer/scope(exit).

Добавлено
Цитата applegame @
Сравни:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res = PQExec(...);
    scope(exit) PQClear(res);

Чисто и любому, деже не умеющему в D, ясно что происходит.

Ага, это пока в очередной раз фигача подобный код ты не забудешь воткнуть свой scope(exit). Нафиг.
Владелец пишется один раз (благодаря умным указателям это иногда просто typedef/using), используется потом много где просто как:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res = PQExec(...);

Сравнивай.

Добавлено
Цитата applegame @
Можно конечно написать RAII обертку, но я давно переболел ООП головного мозга и перестал писать классы/структуры на малейший чих.

ООП не имеет никакого отношения к теме. Совсем.
Я бы даже сказал, что без GC нормального ООП быть не может, но чистые плюсовики меня растерзают, поэтому я не стану этого говорить :D
Обертку во многих случаях даже писать не придется - есть unique_ptr и shared_ptr. Напишешь один using и вперед.

Автор: Qraizer 17.07.20, 15:30
Цитата D_KEY @
Я бы даже сказал, что без GC нормального ООП быть не может, но чистые плюсовики меня растерзают, поэтому я не стану этого говорить
Терзать не буду, но вот путаницу с ООП и ОП отмечу.
Цитата D_KEY @
Обертку во многих случаях даже писать не придется - есть unique_ptr и shared_ptr. Напишешь один using и вперед.
И я вот не понимаю, в чём сложность зафиксировать контракты для компилятора, а не полагаться на бессбойность своих шаловливых рук:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      auto  pq_free = [](PGconn* pCon){ PQFree(pCon); };                    // deleter  коннектов
      using uniquer = std::unique_ptr<PGconn, decltype(pq_free)>;           // владелец коннектов
      auto  creator =[&](PGconn* pCon){ return uniquer(pCon, pq_free); };   // фабрика  владельцев
Я понимаю так, что писать надёжный софт – это, главным образом, наука о его проектировании. Пусть кто-нибудь возьмёт нормальные языки типа Ады, где быстро+качественно+дёшево, а не этот заполонивший рынок ширпотреб, ориентированный на быстро+качественно+дёшево. Ада вон наполовину декларативная, хрен ты там Хелоу, ворлд напишешь, пока все контракты не обозначишь. За натурально декларативные по типу Пролога вообще молчу. Плюсы многословны? Так это в любых надёжных архитектурах так. Но и нужно это один раз, так-то пиши потом
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      /* коннектимся к трём базам */
      auto dbCon1 = creator(PQconnectdb(psql_connect_info1));
      auto dbCon2 = creator(PQconnectdb(psql_connect_info2));
      auto dbCon3 = creator(PQconnectdb(psql_connect_info3));
и не парься. И гарантировано не отхватишь двойных очисток и утечек, как вон с теми scopeами.

Автор: korvin 17.07.20, 15:39
Цитата Qraizer @
Ада вон наполовину декларативная, хрен ты там Хелоу, ворлд напишешь, пока все контракты не обозначишь. За натурально декларативные по типу Пролога вообще молчу

Декларативность как бы ортогональна контрактам и всему такому прочему и Ада даже на половину не декларативна, стандартный императив, разве что с более развитой системой типов, чем много где. Но и там есть дыры, и на Аде космические корабли бороздят просторы бетона.

Автор: Qraizer 17.07.20, 15:40
Так что scope-ы всё равно остаются и навсегда останутся костылями. Когда некому следить за соблюдением контрактов, всегда остаётся вероятность всплыть "Упс! :wub: "ам. Где-то раньше, где-то позже, у кого-то мало, у кого-то много, когда-то редко, когда-то часто, но вероятность всегда будет.
Бывает, что костыли дешевле, потому они и существуют в природе, но делать из костылей технологию – увольте, это детский сад.

Добавлено
Цитата korvin @
Декларативность как бы ортогональна контрактам и всему такому прочему и Ада даже на половину не декларативна, стандартный императив, разве что с более развитой системой типов, чем много где
Ну не придирайся. Ты прекрасно понял, о чём речь. Описание контрактов всегда декларативны по своей природе. Даже обычный прототип функции в простом С декларативно описывает её контракт.

Добавлено
Ну и да, контракты тоже можно описать с ошибками, это не абсолютная панацея.

Автор: korvin 17.07.20, 16:02
Цитата Qraizer @
Ну и да, контракты тоже можно описать с ошибками, это не абсолютная панацея.

Или вообще не описывать =)

https://youtu.be/IiGXq3yY70o?t=782

Автор: JoeUser 18.07.20, 04:49
Цитата Wound @
На крайняк можно юзнуть деструктор смарт поинтера

Так норм? ;)

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <fcntl.h>
    #include <sys/stat.h>
     
    bool ReadFile(std::string iFileName, std::string *iContent) {
      auto close_file = [](int *iFp) {
        close(*iFp);
      };
      auto Fp = std::unique_ptr<int, decltype(close_file)> (
                                new int(open(iFileName.c_str(), O_RDONLY)),
                                close_file);
      int Fd = *Fp.get();
      if (Fd < 0) return false;
      struct stat Sb;
      if (fstat(Fd, &Sb) < 0) return false;
      iContent->resize(Sb.st_size);
      if (read(Fd, const_cast<char *>(iContent->data()), Sb.st_size) < 0) return false;
      return true;
    }

Автор: Wound 18.07.20, 06:28
Цитата JoeUser @
Так норм?

А чем тебя std::ifstream не устроил? :huh: std::unique_ptr - с указателями работает, я бы на крайняк взял уже FILE* fopen/fclose.
А так у тебя костыль какой то + утечка памяти(под int* память выделил, а не освободил).

Добавлено
Если тебе нужно использовать вот только именно эти функции, по религиозным убеждениям, тогда лучше самому написать класс обертку. умные указатели работают как бы с указателями :D

Добавлено
Уж лучше как то так переписать тогда:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      auto close_file = [](FILE* pFile) {
        fclose(pFile);
      };
      auto Fp = std::unique_ptr<FILE, decltype(close_file)> (fopen(iFileName.c_str(), O_RDONLY),close_file);

Но вообще судя по std::string, я бы юзал std::ifstream.

Автор: JoeUser 18.07.20, 06:36
Цитата Wound @
А чем тебя std::ifstream не устроил?

Тормозючий.

Цитата Wound @
А так у тебя костыль какой то + утечка памяти(под int* память выделил, а не освободил).

А еcли так?

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <fcntl.h>
    #include <sys/stat.h>
     
    bool ReadFile(std::string iFileName, std::string *iContent) {
      auto close_file = [](int *iFp) {
        close(*iFp);
        delete iFp;
      };
      auto Fp = std::unique_ptr<int, decltype(close_file)> (
                                new int(open(iFileName.c_str(), O_RDONLY)),
                                close_file);
      int Fd = *Fp.get();
      if (Fd < 0) return false;
      struct stat Sb;
      if (fstat(Fd, &Sb) < 0) return false;
      iContent->resize(Sb.st_size);
      if (read(Fd, const_cast<char *>(iContent->data()), Sb.st_size) < 0) return false;
      return true;
    }

Автор: Wound 18.07.20, 06:41
Цитата JoeUser @
Тормозючий.

Ок, FILE* fopen/fclose - чем не угодил?

Цитата JoeUser @
А еcли так?

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

Добавлено
Нет, оно так конечно будет работать, и все по принципу RAII - но меня лично смущает что ты по факту выделяешь 2 ресурса в одной обертке.

Автор: JoeUser 18.07.20, 06:45
Цитата Wound @
Ок, FILE* fopen/fclose - чем не угодил?

Не такой тормозючий как std::ifstream, но чуть медленнее POSIX варианта. Я не про открытие, а про read/write.

Цитата Wound @
Да костыль это. Ты делаешь какое то левое телодвижение для того, чтоб у тебя скомпилировалось. Это не айс.

Ну нужен мне open, что тут поделать?

Автор: Wound 18.07.20, 06:50
Цитата JoeUser @
Ну нужен мне open, что тут поделать?

Пиши класс обертку тогда. Так будет правильнее. Либо, можно посмотреть какую нить фичу в С++, которая конвертирует тип в указатель.

Автор: JoeUser 18.07.20, 06:51
http://0x80.pl/notesen/2019-01-07-cpp-read...html#computer-1

Автор: Wound 18.07.20, 06:53

В среднем fread быстрее.

Добавлено
Вообще они одинаковы. Видно же. А то что там на пару милисикунд где то POSIX Обогнал - это скорее всего от функций вообще не зависит, винт подзадумался небось вот и разница.

Автор: JoeUser 18.07.20, 07:22
Цитата Wound @
В среднем fread быстрее.

Не, в 4-x из 18 тестов - быстрее. А в остальных 14 - проигрывает.

Добавлено
Цитата Wound @
Нет, оно так конечно будет работать, и все по принципу RAII

Все равно выше у меня было неправильно :lol: Вот теперь должно быть правильно:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <fcntl.h>
    #include <sys/stat.h>
     
    bool ReadFile(std::string iFileName, std::string *iContent) {
      auto close_file = [](int *iFp) {
        if (*iFp>=0) close(*iFp);
        delete iFp;
      };
      auto Fp = std::unique_ptr<int, decltype(close_file)> (
                                new int(open(iFileName.c_str(), O_RDONLY)),
                                close_file);
      int Fd = *Fp.get();
      if (Fd < 0) return false;
      struct stat Sb;
      if (fstat(Fd, &Sb) < 0) return false;
      iContent->resize(Sb.st_size);
      if (read(Fd, const_cast<char *>(iContent->data()), Sb.st_size) < 0) return false;
      return true;
    }

Автор: applegame 18.07.20, 08:01
Цитата Qraizer @
Чё-то я в этом списке Delphi не вижу. Непорядок?
Там написано "и прочее", куда вполне может входить и Delphi. Невнимательность? Впрочем, ты прав, в мое "и прочее" Delphi не входит, но когда-то очень давно туда входил его брат C++ Builder. А что не так с Delphi? :)
Цитата Qraizer @
Это прекрасно, пока не встретишь
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res1 = PQExec(...);
    scope(exit) PQClear(res1);
    auto res2 = PQExec(...);
    scope(exit) PQClear(res1);
    auto res3 = PQExec(...);
    scope(exit) PQClear(res1);
А зачем так писать? Заворачиваем в функцию и вызываем сколько угодно раз не парясь об освобождении ресурса. Смотри ответ Дикею ниже.
Цитата D_KEY @
Я тоже пробовал много чего, в том числе и то, что указал ты (кроме elixir, да и D мало слишком).
Я не считаю C++ лучшим или что-то такое, но вот RAII очень крутая (и при этом простая) концепция.
Конечно. RAII - это прекрасная технология, особенно если нужен именно объект владеющий ресурсом. А вот в случае когда нужен скоуп владеющий ресурсом, RAII зачастую совершенно излишен. Ты, видимо, совсем мало использовал ФП иначе знал бы об этом. Угадай, как функциональщики без всяких RAII управляют, допустим, транзакциями в БД? :)
Цитата D_KEY @
Ага, это пока в очередной раз фигача подобный код ты не забудешь воткнуть свой scope(exit). Нафиг.
Владелец пишется один раз (благодаря умным указателям это иногда просто typedef/using), используется потом много где просто как:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto res = PQExec(...);

Сравнивай.
:D Инерция мышления, однако. Очевидно, если захват и освобождение ресурса необходимо повторить много раз, пишется обертка, только не класс, а функция, которая нередко (но не всегда) оказывается проще и понятнее RAII аналога:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Result exec(...) {
      auto res = PQExec(...);
      scope(exit) PQClear(res);
      ...
      return Result(res);
    }
     
    auto res = exec(...);

Сравнивай. :lol:
В таком случае вероятность забывания написания scope равна вероятности забывания написания деструктора. :)
Цитата D_KEY @
ООП не имеет никакого отношения к теме. Совсем.
Я бы даже сказал, что без GC нормального ООП быть не может, но чистые плюсовики меня растерзают, поэтому я не стану этого говорить
Обертку во многих случаях даже писать не придется - есть unique_ptr и shared_ptr. Напишешь один using и вперед.
Сам по себе ООП нет, а вот образ мышления, который навязывает мейнстримовый ООП (который на трех китах) очень даже имеет. Ну и не забываем, что scope() может рулить чем угодно, например, теми же транзакциями в БД или мьютексами, а всякие xxxx_ptr только указателями.
Цитата Wound @
Я же ссылку привел выше, ок продублирую еще раз:
https://forum.dlang.org/thread/kjkdfcnpgdeg...forum.dlang.org

Вот тут чувак выхватил сигфолт благодаря GC и scope(exit).
Прочитал. scope(exit) там не причем. С тем же успехом можно сегфолтнуть и в деструкторе RAII обертки. Чувак просто не знал, что в D (как и в Java и, наверное, в C#, поправь если не так) объекты на куче автоматически не дестроятся при выходе из скоупа, так как этим занимается GC. А вот объекты на стеке дестроятся автоматически как и в C++.

Автор: Wound 18.07.20, 08:10
Цитата applegame @
Прочитал. scope(exit) там не причем. С тем же успехом можно сегфолтнуть и в деструкторе RAII обертки. Чувак просто не знал, что в D (как и в Java и, наверное, в C#, поправь если не так) объекты на куче автоматически не дестроятся при выходе из скоупа, так как этим занимается GC. А вот объекты на стеке дестроятся автоматически как и в C++.

Нет, не с тем же успехом. Если убрать его багу, допустим выкинуть scope(exit) вообще из его программы, никаких сигфолтов нет. Его программа абсолютна правильная и рабочая на языке D.
Но не в С++, вот в этом и состоит существенная разница. Для С++ эта программа багнутая. Поэтому в D - ты можешь выхватить очень просто этот сигфолт, потому что тебе нужно этот ньюанс держать в голове.
А в С++, если следовать всем правилам - такой ситуации просто не получится. Потому что раз там указатель и память выделяется по new, значит это так же оборачивается в RAII обертку. А она сама знает когда нужно вызвать деструктор.

Ну я не знаю с чем ты споришь. В Dшном примере - ручное управление ресурсом в перемешку с автоматическим GC. В С++ если это все обернуть в RAII обертки - полностью автоматическое управление ресурсом.

Добавлено
Если я неясно выразился, поясню на примере:
Возьмем вариант D. Было:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
     
    struct De
    {
        ~this() { writeln("De"); }
    }
     
    void main()
    {
        De a;
        scope(exit) writeln("scope exit");
        De b;
    }

Все работало правильно.
Потом пришел Вася, и с бодуна поправил какой то баг:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
     
    struct De
    {
        ~this() { writeln("De"); }
    }
     
    void main()
    {
        auto a = new De();
        scope(exit) writeln("scope exit");
        De b;
    }

И опля - породил еще один баг. Вася просто не увидел scope(exit), думал о другом, еще что то там.
А в С++ если вася так исправит - это не пройдет код ревью, либо он заведомо напишет баг, память по new выделил, а удалить то забыл, тю тю. А если вася грамотный, он естественно будет писать RAII обертки, и у него такой ситуации просто не возникнет.

Автор: applegame 18.07.20, 08:21
Цитата Wound @
Ручное управление ресурсом - всегда хуже автоматического, потому как является источником многих косяков и багов.
Ну так в моем случае оно тоже автоматическое. Точнее, я только в одном месте использую scope, а далее просто вызываю функцию. Точно так же ты, делая обертку в дуструкторе вручную освобождаешь ресурсы. Вопрос исключительно в простоте, понятности и читабельности. В C++ у тебя особо-то и выбора нет, в отличие от того же D, где ты можешь использовать и RAII и scope(exit) в зависимости от ситуации. С появлением в плюсах лямбд стало несколько попроще, некоторые плюсовики даже внезапно открыли для себя ФП техники давно применяющиеся в том же D :lol:. Но лямбды в плюсах, как и все остальное, получились страшными, как атомная война. Поэтому, видимо, с таким скрипом и удивлением люди узнают, что помимо их любимых, казавшихся единственными и самыми правильными техниками, существуют и другие более простые, понятные и не менее безопасные техники.
Цитата Qraizer @
Так что scope-ы всё равно остаются и навсегда останутся костылями.
Как я тебя понимаю, сам таким был несколько лет назад. :D

Автор: JoeUser 18.07.20, 08:22
Цитата Wound @
Пиши класс обертку тогда.

В принципе, я помню пост Qraizer'а. Но мне кажется, что по накладным расходам они очень близки. Но у него Guard на стеке. Поэтому переделываем еще, чтобы быть не хуже :lol:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    bool ReadFile(std::string iFileName, std::string *iContent) {
      int Place;
      auto close_file = [](int *iFp) {
        if (*iFp >= 0) close(*iFp);
      };
      auto Fp = std::unique_ptr<int, decltype(close_file)> (
                  new (&Place) int(open(iFileName.c_str(), O_RDONLY)),
                  close_file);
      int Fd = *Fp.get();
      if (Fd < 0) return false;
      struct stat Sb;
      if (fstat(Fd, &Sb) < 0) return false;
      iContent->resize(Sb.st_size);
      if (read(Fd, const_cast<char *>(iContent->data()), Sb.st_size) < 0) return false;
      return true;
    }


И утечки нет, и все на стеке сидит (за исключением хитрого указателя, у него кишки небойсь в куче?). :victory:

Автор: Wound 18.07.20, 08:25
Цитата applegame @
Ну так в моем случае оно тоже автоматическое. Точнее, я только в одном месте использую scope, а далее просто вызываю функцию. Точно так же ты, делая обертку в дуструкторе вручную освобождаешь ресурсы. Вопрос исключительно в простоте, понятности и читабельности. В C++ у тебя особо-то и выбора нет, в отличие от того же D, где ты можешь использовать и RAII и scope(exit) в зависимости от ситуации. С появлением в плюсах лямбд стало несколько попроще, некоторые плюсовики даже внезапно открыли для себя ФП техники давно применяющиеся в том же D :lol:. Но лямбды в плюсах, как и все остальное, получились страшными, как атомная война. Поэтому, видимо, с таким скрипом и удивлением люди узнают, что помимо их любимых, казавшихся единственными и самыми правильными техниками, существуют и другие более простые, понятные и не менее безопасные техники.

Чем твоя функция будет отличаться от Сишной функции с теми же goto ?

Добавлено
Цитата applegame @
Точно так же ты, делая обертку в дуструкторе вручную освобождаешь ресурсы. Вопрос исключительно в простоте, понятности и читабельности. В C++ у тебя особо-то и выбора нет, в отличие от того же D, где ты можешь использовать и RAII и scope(exit) в зависимости от ситуации.

Я думаю, что если бы этот scope(exit) был таким же крутым как RAII, то во всяких Java/C# он давно бы уже был, вместо этих ваших using/try-with-resources, которые имитируют как раз RAII, с принудительным вызовом деструктора.

Добавлено
Потому что как показывает практика, выделять ресурс никто не забывает в основном, забывают написать освобождение ресурса, либо делают его не в том порядке, либо сама программа спроектирована так, что вызывает эти функции очистки не в том порядке, что приводит к ошибкам. Конечно - когда ты тут показываешь 3 строчки кода - тут все красиво и понятно, а когда есть большой проект, то становится все не так очевидно.
В случае с RAII ты пишешь инициализацию/удаление отдельно, и забыть написать удаление - конечно можно, но это довольно не тривиальная задача. А потом просто юзай и все, не заботясь о том в каком там порядке что у тебя вызывается, или когда там нужно очистить если исключение произошло или еще что то, сам компилятор за тебя все очистит правильно и гарантированно в нужном порядке.

Автор: applegame 18.07.20, 08:46
Цитата Wound @
Нет, не с тем же успехом. Если убрать его багу, допустим выкинуть scope(exit) вообще из его программы, никаких сигфолтов нет. Его программа абсолютна правильная и рабочая на языке D.
Нет его программа неправильная. Он неверно оценил время жизни объектов. Представь себе, что чувак написал вместо scope() RAII-обертку, в деструкторе которой вызвал этот свой TTF_Quit(). Все также грохнется без всякого scope().
Цитата Wound @
Но не в С++, вот в этом и состоит существенная разница. Для С++ эта программа багнутая.
Я хз, как ты оценил багнутость дешной программы с точки зрения C++. Эти языки хоть и сильно похожи, но также и сильно разные. В D можно вообще не освобождать память и утечек не будет, а для C++ утечки будут, и что, эти программы будут багнутые? Бессмыслица какая-то.
Цитата Wound @
В Dшном примере - ручное управление ресурсом в перемешку с автоматическим GC.
Именно, вот это главная причина, чувак вступил на теневую территорию, а там легко выстрелить себе в ногу. Аналогично было у меня, когда я попытался перемешать shared_ptr со своими RAII обертками. Это было когда я изучал boost::asio. Забавно, но в относительно сложных системах вроде boost::asio, shared_ptr фактически теряет свою детерминированность и становится очень похож на обычный GC. Очень похоже на вот этот твой пример.
Цитата Wound @
Ну я не знаю с чем ты споришь. В Dшном примере - ручное управление ресурсом в перемешку с автоматическим GC.
Я спорю с тем, что scope(exit) тут не причем. scope(exit) просто выполняет код при выходе из скоупа - все. Если этот код некорректен и приводит к сегфолту, то причем тут сам scope(exit)? Допустим ты написал цикл for в теле которого сделал что-то нехорошее и оно у тебя упало. По твоей логике получается, что цикл for опасен и применять его нужно с осторожностью.

Добавлено
Цитата Wound @
Чем твоя функция будет отличаться от Сишной функции с теми же goto ?
Тем что в моей функции нет goto? :-?
Цитата Wound @
Я думаю, что если бы этот scope(exit) был таким же крутым как RAII, то во всяких Java/C# он давно бы уже был, вместо этих ваших using/try-with-resources, которые имитируют как раз RAII, с принудительным вызовом деструктора.
Возможно и появятся. Ты же нашел экспериментальныек scope в плюсах. :) Кроме того, еще есть scope(success) и scope(failure).
Цитата Wound @
В случае с RAII ты пишешь инициализацию/удаление отдельно, и забыть написать удаление - конечно можно, но это довольно не тривиальная задача. А потом просто юзай и все, не заботясь о том в каком там порядке что у тебя вызывается, или когда там нужно очистить если исключение произошло или еще что то, сам компилятор за тебя все очистит правильно и гарантированно в нужном порядке.
Тут похоже, ты один раз пишешь функцию-обертку, отлаживаешь ее (точно так же как и ты свою RAII-обертку) и используешь много раз не заботясь об освобождении ресурса. Повторю, это не всегда удобнее традиционного RAII.
Я ничего не имею против RAII и сам им актовно пользуюсь. Но я привожу примеры, где scope(exit) проще и понятнее и не менее безопасно, чем RAII. Вы же мне в ответ приводите другие примеры, где RAII уместнее. Получается странный и бестолковый спор.

Добавлено
Ну и наконец. Прошу все же учесть, что scope(...) это не только про RAII. scope это скорее про try/catch/finally. Вот коротенькая глава из книжки по D о скоупах, если интересно:
http://ddili.org/ders/d.en/scope.html

Автор: Wound 18.07.20, 09:31
Цитата applegame @
Нет его программа неправильная. Он неверно оценил время жизни объектов. Представь себе, что чувак написал вместо scope() RAII-обертку, в деструкторе которой вызвал этот свой TTF_Quit(). Все также грохнется без всякого scope().

Ну как не правильная, я же говорю не про текущий пример, а вообще выкинь scope(exit) - его программа правильная, я имею ввиду что ему не нужно вызывать потом delete a; т.к. у него есть GC, соответственно он на него и положился. В случае с С++ такой ситуации не будет, потому что не будет scope(exit), вместо него будет своя RAII обертка. Далее ресурс который должен уничтожиться до scope(exit) - будет агрегатом этой RAII обертки.

Цитата applegame @
Я хз, как ты оценил багнутость дешной программы с точки зрения C++. :lol: Эти языки хоть и сильно похожи, но также и сильно разные. В D можно вообще не освобождать память и утечек не будет, а для C++ утечки будут, и что, эти программы будут багнутые? Бессмыслица какая-то.

А что там оценивать? Вот смотри:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        import std.stdio;
        
        struct De
        {
            ~this() { writeln("De"); }
        }
        
        void main()
        {
            auto a = new De();
        }

Тут есть ошибки, если писать на D? В С++ в таком виде есть ошибка, а в D?


Цитата applegame @
Именно, вот это главная причина, чувак вступил на теневую территорию, а там легко выстрелить себе в ногу. Аналогично было у меня, когда я попытался перемешать shared_ptr со своими RAII обертками. Это было когда я изучал boost::asio. Забавно, но в относительно сложных системах вроде boost::asio, shared_ptr фактически теряет свою детерминированность и становится очень похож на обычный GC. Очень похоже на вот этот твой пример.

Я что то не понял, в чем ошибка? У shared_ptr есть своя болезнь с циклическими ссылками.

Цитата applegame @
Я спорю с тем, что scope(exit) тут не причем. scope(exit) просто выполняет код при выходе из скоупа - все. Если этот код некорректен и приводит к сегфолту, то причем тут сам scope(exit)? Допустим ты написал цикл for в теле которого сделал что-то нехорошее и оно у тебя упало. По твоей логике получается, что цикл for опасен и применять его нужно с осторожностью.

Ну как не причем? Представь его бы не было. Как бы ты все это писал? Естественно на RAII каком нить, и у тебя такой ситуации просто не возникло бы.

Вот возьми goto - чем он плох? Он такой же как return/continue/break/for/while/etc, он ничем не хуже этих операторов. Но его почему то все ругают. А потому что если им увлечься, то ты будешь писать "как проще", но совсем не "качественно", да вон даже отмотай на 5 страниц назад. Чувак пишет - накой мне писать и городить классы и какие то обертки, если я взял вызвал goto clean: и все. 10 букв, против целого класса!!! Так же и с этим. Тебе не нужно думать и замарачиваться каким то обертками, ты взял вот тут юзнул scope(exit), а в другом месте забыл его вызвать. И вуаля.

Цитата applegame @
Тем что в моей функции нет goto?

Т.е. по сути твой пример со scope(exit) ничем не отличается от того же goto ? :D Замени scope(exit) на goto clean: в функции PQExec, да или вообще просто чем вот это:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        Result exec(...) {
          auto res = PQExec(...);
          scope(exit) PQClear(res);
          ...
          return Result(res);
        }
        
        auto res = exec(...);

Отличается вот от этого:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        Result exec(...) {
          auto res = PQExec(...);
          Result result(res);
     
          PQClear(res);
          ...
          return result;
        }
        
        auto res = exec(...);

???

Цитата applegame @
Возможно и появятся. Ты же нашел экспериментальныек scope в плюсах.

Да потому что в плюсы тащат в последнее время то, что нафиг не уперлось, всякий шлак тупо.

Цитата applegame @
Тут похоже, ты один раз пишешь функцию-обертку, отлаживаешь ее (точно так же как и ты свою RAII-обертку) и используешь много раз не заботясь об освобождении ресурса.

Чем это от Сишной какой нибудь функции отличается?

Хорошо, вот есть у тебя файловый ресурс, покажи как ты это реализуешь? Открыть файл, почитать его, потом что то поделать с вычитанными данными, потом снова почитать, потом записать в него, потом почитать из него, потом закрыть его. Через запятую перечислены отдельные операции, каждая операция - отдельная функция. Потом все это нужно сделать в 10 различных местах.
Покажи как мне это сделать со scope(exit)?

Добавлено
Цитата applegame @
Но я привожу примеры, где scope(exit) проще и понятнее и не менее безопасно, чем RAII. Вы же мне в ответ приводите другие примеры, где RAII уместнее. Получается странный и бестолковый спор.

Я тебе об этом писал еще раньше:
Цитата Wound @
Важно то, что тот контекст в котором ты говоришь, что якобы удобно писать scope(exit) вот тут - это удобно при твоей архитектуре приложения и удобно в том языке, в котором ты пишешь. Т.е. это не фича, что без нее вот тут будет плохо, это просто ты так написал свою программу. Я например напишу так, что в scope(exit) не будет никакого смысла, другой напишет так, что без goto не обойдешься.

Все верно, ты просто выдумываешь примеры причем на D, где уместно будет юзнуть scope(exit), но в С++ - это же будет называться костылем. Тебе об этом как бы и пишут.

Цитата applegame @
Ну и наконец. Прошу все же учесть, что scope(...) это не только про RAII. scope это скорее про try/catch/finally. Вот коротенькая глава из книжки по D о скоупах, если интересно:

Тото и оно, что все эти try/catch/finally - как раз и являются костылями в языках с GC, потому что там нельзя гарантировать вызов деструктора в определенное время и в определенном месте. Поэтому там просто без try/catch/finally - ты не сможешь нормально писать программу. Даже файл не закроешь нормально. А в С++ это нафиг не нужно, потому что там есть деструкторы.
И об этом я тебе тоже писал:
Цитата Wound @
Это как знаешь, сказать в C#/Java есть ах**наая плюшка finally, а в С++ ее нет, бе бе бе. Вы мудохайтесь там теперь и живите с этим, а я finally юзаю и доволен. Ну как то вот так это выглядит

Автор: applegame 18.07.20, 10:33
Цитата Wound @
Ну как не правильная, я же говорю не про текущий пример, а вообще выкинь scope(exit) - его программа правильная, я имею ввиду что ему не нужно вызывать потом delete a; т.к. у него есть GC, соответственно он на него и положился.
Ну напиши в scope(exit) вместо TTF_Quit(), witeln("Hello, world") и тоже все будет норм. Ты реально не понимаешь, что проблема вот в этом лишнем TTF_Quit(), а не в scope(exit)?
Цитата Wound @
Тут есть ошибки, если писать на D? В С++ в таком виде есть ошибка, а в D?
В D нет ошибки, продолжай свою мысль.
Цитата Wound @
Т.е. по сути твой пример со scope(exit) ничем не отличается от того же goto ? :D Замени scope(exit) на goto clean: в функции PQExec, да или вообще просто чем вот это:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        Result exec(...) {
          auto res = PQExec(...);
          scope(exit) PQClear(res);
          ...
          return Result(res);
        }
        
        auto res = exec(...);

Отличается вот от этого:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        Result exec(...) {
          auto res = PQExec(...);
          Result result(res);
     
          PQClear(res);
          ...
          return result;
        }
        
        auto res = exec(...);

???
Тем, что во втором варианте, в случае возникновения исключения между auto res = PQExec(...) и PQClear(res), последний не будет вызван и получится утечка. А в первом PQClear(res) будет вызвн в любом случае. Может это то самое отличие от goto, о котором ты спрашиваешь?
Цитата Wound @
Да потому что в плюсы тащат в последнее время то, что нафиг не уперлось, всякий шлак тупо.
Да в последнее время вообще придумывают всякий шлак вроде C#. :)
Цитата Wound @
Ну как не причем? Представь его бы не было. Как бы ты все это писал? Естественно на RAII каком нить, и у тебя такой ситуации просто не возникло бы.
:facepalm: Ну давай я тебе напишу кривой аналог с RAII вместо scope(exit) с такими же сегфолтом:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct TTFGuard {
        this() { TTF_Init(); }
        ~this() { TTF_Quit(); }
    }
     
    voiud main() {
        auto a = new SDL;
        TTFGuard guard;
    }

Убери TTFGuard guard; И сегфолт пропадет. Прямо как со scope(exit). По-твоему, получается, что RAII опасно и его нужно использовать осторожно?
Цитата Wound @
Чем это от Сишной какой нибудь функции отличается?
Я не понимаю что ты хочешь от меня услышать. scope(exit) и goto это совершенно разные вещи. goto - безусловный переход, scope вставляет указанный код в конец функции.
Цитата Wound @
Все верно, ты просто выдумываешь примеры причем на D, где уместно будет юзнуть scope(exit), но в С++ - это же будет называться костылем. Тебе об этом как бы и пишут.
Во-первых, я не выдумываю, это реальные примеры. Во-вторых, scope(exit) в описываемых мной случаях был бы удобней и в C++, если бы он там был, конечно. Возможно скоро появится :), но все равно будет уродливым. Объявлять переменные, которые потом никак явно не используются в коде - какая гадость.
Цитата Wound @
Тото и оно, что все эти try/catch/finally - как раз и являются костылями в языках с GC, потому что там нельзя гарантировать вызов деструктора в определенное время и в определенном месте. Поэтому там просто без try/catch/finally - ты не сможешь нормально писать программу. Даже файл не закроешь нормально. А в С++ это нафиг не нужно, потому что там есть деструкторы.
Ок, так и запишем, Киля считает что try и catch - это костыли для языков с GC и в C++ не нужны. Но на всякий случай, я уточню, ты наверное таки имел в виду только finally, а не try/catch/finally?

Автор: korvin 18.07.20, 10:34
Цитата Wound @
try/catch/finally - как раз и являются костылями в языках с GC

Но в Delphi есть finally, но нет GC…

Автор: applegame 18.07.20, 10:46
Цитата Wound @
Хорошо, вот есть у тебя файловый ресурс, покажи как ты это реализуешь? Открыть файл, почитать его, потом что то поделать с вычитанными данными, потом снова почитать, потом записать в него, потом почитать из него, потом закрыть его. Через запятую перечислены отдельные операции, каждая операция - отдельная функция. Потом все это нужно сделать в 10 различных местах.
Покажи как мне это сделать со scope(exit)?
"в 10 различных местах" - это в разных местах одной функции или что? Приведи лучше свой вариант с RAII (не обязательно c реально существующими названиями классов, можно гипотетически), а я приведу аналог со scope.

Добавлено
Цитата korvin @
Но в Delphi есть finally, но нет GC…

А в D, языке с GC, есть такие же гарантии как и в C++, но finally тоже есть. Правда я не видел, чтобы кто-то его использовал. :)

Автор: Wound 18.07.20, 11:00
Цитата applegame @
Ну напиши в scope(exit) вместо TTF_Quit(), witeln("Hello, world") и тоже все будет норм. Ты реально не понимаешь, что проблема вот в этом лишнем TTF_Quit(), а не в scope(exit)?

:wacko: Ты точно читал что там на форуме обсуждалось?
https://forum.dlang.org/post/kjkdfcnpgdegno...forum.dlang.org
У него проблема не в лишнем TTF_Quit, у него проблема в порядке вызовов функций:
Цитата

I'm currently working on a project and for that I've created a thin OO-wrapper on top of derelict-sdl. However, when I close my app, the program terminates with a segfault. I've managed to track down the source, and found that the destructors of my objects are called AFTER the scope(exit) statements. This causes my program to call TTF_CloseFont() after TTF_Quit(), resulting in a segfault.

Гугл перевод:
В настоящее время я работаю над проектом, и для этого я создал тонкий OO-упаковщик поверх derelict-sdl. Однако когда я закрываю свое приложение, программа завершается с ошибкой. Мне удалось отследить источник, и обнаружил, что деструкторы моих объектов называются ПОСЛЕ scope(exit). Это заставляет мою программу вызывать TTF_CloseFont () после TTF_Quit (), в результате чего возникает ошибка.

Он ожидает что у него отработает деструктор его класса De, в котором он вызывает TTF_CloseFont, а после него scope(exit) - который вызывает TTF_Quit. Но из за того что память под класс он выделил на хипе, у него вызывается сначало scope(exit) с TTF_Quit, а после отрабатывает деструктор класса De, в котором вызывается TTF_CloseFont. Где тут лишний вызов то?

Цитата applegame @
В D нет ошибки, продолжай свою мысль.

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


Цитата applegame @
Тем, что во втором варианте, в случае возникновения исключения между auto res = PQExec(...) и PQClear(res), последний не будет вызван и получится утечка. А в первом PQClear(res) будет вызвн в любом случае. Может это то самое отличие от goto, о котором ты спрашиваешь?

Это же Си, там нет исключений! Если разница только в исключениях, то твоей пример ничем не отличается от сишного кода. А объяснять что подход с RAII лучше, чем Сишный нужно? Или ты и так знаешь?

Цитата applegame @
Да в последнее время вообще придумывают всякий шлак вроде C#.

И чем же он плох? По моему очень даже хороший ЯП.

Цитата applegame @
:facepalm: Ну давай я тебе напишу кривой аналог с RAII вместо scope(exit) с такими же сегфолтом:

Ага, багнутый аналог! В С++ там не пишут. Ты один ресурс в обертку обернул, а второй ресурс без обертки юзаешь. Здорово! :good:

Цитата applegame @
Убери TTFGuard guard; И сегфолт пропадет. Прямо как со scope(exit). По-твоему, получается, что RAII опасно и его нужно использовать осторожно?

Ну хватит а? Хватит писать какую то муть. Ты если оборачиваешь в RAII - Оборачивай все. А то у тебя получается херня какая то. Я тоже могу написать какую нибудь неработающую херню на D и сказать - оцтой какойто, а не ЯП.

Цитата applegame @
Я не понимаю что ты хочешь от меня услышать. scope(exit) и goto это совершенно разные вещи. goto - безусловный переход, scope вставляет указанный код в конец функции.

Ну а goto делает переход, например в конец функции. Ты представляешь этот scope(exit) - как какую то супер пупер фичу, на деле - это обычный костыль, судя по всему введен в язык исключительно с целью освобождения ресурсов при работе с GC. Это единственно верное его предназначение. Но ты выдаешь его зачем то за какую то конфетку :-?

Цитата applegame @
Во-первых, я не выдумываю, это реальные примеры. Во-вторых, scope(exit) в описываемых мной случаях был бы удобней и в C++, если бы он там был, конечно. Возможно скоро появится :), но все равно будет уродливым. Объявлять переменные, которые потом никак явно не используются в коде - какая гадость.

В описываемых тобой случаях - там где он был бы удобен, он был бы удобен исключительно в качестве костыля.


Цитата applegame @
Ок, так и запишем, Киля считает что try и catch - это костыли для языков с GC и в C++ не нужны. И да, в D, языке с GC, такие гарантии есть, как и в C++.

Не перевирай, я про try/catch - слова не сказал. Или ты не знаешь что такое try/catch/finally?


Цитата applegame @
Но на всякий случай, я уточню, ты наверное таки имел в виду только finally, а не try/catch/finally?

Поясняю - я имел ввиду try/catch/finally, а не finally. На всякий случай посоветую тебе, ты прежде чем отвечать на это, загугли про это и почитай что такое finally и как оно используется в языках с GC JAVA/C#. Потом уже отвечай.
Я не знаю как там в вашем D, но вообще конструкция try/catch/finally судя по всему родилась где то в Java, И вот этот finally - является не отъемлемой частью try/catch/finally, нельзя его написать без try/catch.
Поэтому и называется она try/catch/finally. В C# есть аналогичная конструкция, но там быстро смекнули как этим херово было пользоваться в Java, и у себя сделали конструкцию using(var resource = new Resource()){}
Которая по сути при выходе из блока вызывает Dispose, для освобождения ресурсов. В Java посоны плакали, кололись, но продолжали жрать кактус, пока потом в одной из последних версий не ввели свою конструкцию try-with-resources.

Цитата korvin @
Но в Delphi есть finally, но нет GC…

Так в Delphi деструкторов потому что нет.

Добавлено
Цитата applegame @
в 10 различных местах" - это в разных местах одной функции или что? Приведи лучше свой вариант с RAII (не обязательно c реально существующими названиями классов, можно гипотетически), а я приведу аналог со scope.

Ну посмотри на класс std::fstream - обычная RAII Обертка над FILE* дескриптором. В 10 разных местах я хочу юзать вот эти функции, ну например у меня есть 10 кнопок, и в каждом обработчике я хочу производить вот примерно такие действия, разные и в разной последовательности, допустим в обработчике одной кнопки только читаем, во второй читаем и пишем, в третьей читаем, создаем новый файл в него пишем и т.д.. Т.е. мне нужны какие то функции или что ты там сделаешь, за которыми мне потом не надо подчищать, они же сами все сделают.

Вообще не парься, у тебя не получится это сделать со scope(exit), либо получится говно какое то, в итоге ты просто напишешь RAII обертку. Что собственно и требовалось доказать.

Автор: korvin 18.07.20, 11:21
Цитата Wound @
Так в Delphi деструкторов потому что нет.

Как это нет, когда есть?

Автор: Wound 18.07.20, 11:24
Цитата korvin @
Как это нет, когда есть?

То что оно называется гордым словом destructor еще не означает что это аналог привычных плюсовых деструкторов. Уже ломали же копья по этому вопросу. В делфи есть инициализаторы и деинициализаторы. А вся модель конструктор/деструктор основана на некой фабрике объектов. Я тоже могу написать какое нибудь API на плюсах, и гдето там в базовом методе назвать метод Destructor, вместо Release, и че - это деструктором станет в привычном понимании этого слова?

Автор: applegame 18.07.20, 13:35
Цитата Wound @
Он ожидает что у него отработает деструктор его класса De, в котором он вызывает TTF_CloseFont, а после него scope(exit) - который вызывает TTF_Quit. Но из за того что память под класс он выделил на хипе, у него вызывается сначало scope(exit) с TTF_Quit, а после отрабатывает деструктор класса De, в котором вызывается TTF_CloseFont. Где тут лишний вызов то?
Ты сам сказал, что если убрать совсем scope(exit), то программа станет корректной. А ведь в этом случае TTF_Quit не будт вызван вообще. Из твоих же слов следует, что вызов TTF_Quit лишний.
Чувак по незнанию неверно оценил ситуацию, он написал:
Цитата
AFTER the scope(exit) statements
А точнее было бы написать
Цитата
AFTER exit from the function scope
scope(exit) никак не влияет на порядок вызовов деструкторов объектов на куче.
Цитата Wound @
Вот именно. Поэтому ты без задней мысли будешь думать что ошибки у тебя нет. А в плюсах это баг априори. Соответственно ты ничего не будешь ждать, ты напишешь RAII обертку, и бага не будет.
Напиши scope(exit) delete(ptr) и бага не будет. По-твоему написать целую обертку гораздо проще, чем написать вот эту одну строчку, с учетом того что эта обертка будет использована ровно ОДИН раз?
Цитата Wound @
Это же Си, там нет исключений! Если разница только в исключениях, то твоей пример ничем не отличается от сишного кода. А объяснять что подход с RAII лучше, чем Сишный нужно? Или ты и так знаешь?
Очередной ошибочный вывод. Нет не только в исключениях. Также разница в ранних return, в том что код освобождения ресурса пишется прямо рядом с его захватом (прямо как твой любимый unique_ptr с кастомным деструктром, только гораздо опрятнее), ну и если таких мест несколько, то освобождение ресурсов будет выполнено автоматически в порядке обратном захвату. В сяшке ничего этого нет.
Цитата Wound @
Ага, багнутый аналог! В С++ там не пишут. Ты один ресурс в обертку обернул, а второй ресурс без обертки юзаешь. Здорово!
Ну так и пример багнутый. В D так не пишут. Чувак один ресурс обернул в scope(exit) а воторой доверил GC. Здорово!
Цитата Wound @
Ну хватит а? Хватит писать какую то муть. Ты если оборачиваешь в RAII - Оборачивай все. А то у тебя получается херня какая то.
Ну хватит а? Хватит писать какую то муть. Ты если оборачиваешь в scope(exit) - Оборачивай все. А то у тебя получается херня какая то.
Цитата Wound @
Я тоже могу написать какую нибудь неработающую херню на D и сказать - оцтой какойто, а не ЯП.
А ты примерно так и сделал. Притащил какую-то неработающую херню со scope(exit) и сказал - оцтой какойто.
Цитата Wound @
Ну а goto делает переход, например в конец функции. Ты представляешь этот scope(exit) - как какую то супер пупер фичу, на деле - это обычный костыль, судя по всему введен в язык исключительно с целью освобождения ресурсов при работе с GC. Это единственно верное его предназначение. Но ты выдаешь его зачем то за какую то конфетку
Неверно, scope(...) введен в язык для автоматического вызова кода при выходе из скоупа. А при работе с GC ресурсы обычно освобождаются самим GC. Накой там scope(...)?
Цитата Wound @
В описываемых тобой случаях - там где он был бы удобен, он был бы удобен исключительно в качестве костыля.
Вот годный пример использования scope(...). Нужно сделать подсчет текущих активных и успешных http-запросов в http-сервере:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto handleHTTPRequest(...) {
        activeRequests.atomicIncrement();
        scope(exit) activeRequests.atomicDecrement();
        scope(success) successRequests.atomicIncrement();
        ...
        if(...) return result1;
        ...
        if(...) throw Error1;
        ...
        if(...) return result2;
        ...
        if(...) throw Error2;
        ...
    }
И где тут костыль, даже если это написано на плюсах?
Цитата Wound @
Поясняю - я имел ввиду try/catch/finally, а не finally. На всякий случай посоветую тебе, ты прежде чем отвечать на это, загугли про это и почитай что такое finally и как оно используется в языках с GC JAVA/C#. Потом уже отвечай.
Я не знаю как там в вашем D, но вообще конструкция try/catch/finally судя по всему родилась где то в Java, И вот этот finally - является не отъемлемой частью try/catch/finally, нельзя его написать без try/catch.

Тебя трудно понять, Киля. Ты применяешь какие-то определения, выдавая их за общепринятые и потом тебя не понимают. Видимо ты имеешь в виду конструкцию, которую в C# называют try-catch-finally. Ну так ты опять ошибся, так как finally вполне можно написать без try-catch, а именно try-finally без catch.
Цитата Wound @
Ну посмотри на класс std::fstream - обычная RAII Обертка над FILE* дескриптором. В 10 разных местах я хочу юзать вот эти функции, ну например у меня есть 10 кнопок, и в каждом обработчике я хочу производить вот примерно такие действия, разные и в разной последовательности, допустим в обработчике одной кнопки только читаем, во второй читаем и пишем, в третьей читаем, создаем новый файл в него пишем и т.д.. Т.е. мне нужны какие то функции или что ты там сделаешь, за которыми мне потом не надо подчищать, они же сами все сделают.
В таком случае, да. RAII самое то.
Цитата Wound @
Вообще не парься, у тебя не получится это сделать со scope(exit), либо получится говно какое то, в итоге ты просто напишешь RAII обертку. Что собственно и требовалось доказать.
:lol: Ты привел пример, в котором RAII вполне уместен. Но я таки напишу специально для тебя все это дело в функциональном стиле без всякого RAII, но со scope(exit), дабы, так сказать, расширить твой и не только твой кругозор :)
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // Это функция обертка, вместо RAII-объекта
    void useFile(F fn)(string name, F fn) {
        auto file = open(name);
        scope(exit) close(file);
        fn(file);
    }
     
    void onClick(data) {
        // используем обертку
        useFile("hello.txt", (file) {
            ...
            write(file, data);
            ...
            data1 = read(file);
            ...
        });
    }

Автор: Wound 18.07.20, 14:19
Цитата applegame @
Ты сам сказал, что если убрать совсем scope(exit), то программа станет корректной. А ведь в этом случае TTF_Quit не будт вызван вообще. Из твоих же слов следует, что вызов TTF_Quit лишний.

Я тебе это говорил в контексте правильности/неправильности такого подхода на плюсах, чтоб обратить твое внимание на GC.

Цитата applegame @
Чувак по незнанию неверно оценил ситуацию, он написал:
Цитата
AFTER the scope(exit) statements
А точнее было бы написать
Цитата
AFTER exit from the function scope
scope(exit) никак не влияет на порядок вызовов деструкторов объектов на куче.

Он как раз все верно оценил, если я правильно понял что там произошло. Ты же видишь в его примере просто надпись в консоль выводится? У него вывелось:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    scope exit
    De
    De

А произошло это из за того, что после выхода из функции - не отработал деструктор De, который написан перед scope(exit), этот деструктор отработал в тот момент, когда GC начал подчищать ресурсы, а ресурсы в его случае он начал подчищать, когда он нажал на кнопку Закрыть.

сам по себе scope(exit) - Не влияет на порядок вызовов деструкторов, я нигде не утверждал что он влияет. Но используя этот scope(exit) - можно нарваться вот на такие грабли, как у чувака, вот об этом я и написал.

Цитата applegame @
Напиши scope(exit) delete(ptr) и бага не будет. По-твоему написать целую обертку гораздо проще, чем написать вот эту одну строчку, с учетом того что эта обертка будет использована ровно ОДИН раз?

Ну да, нахер юзать все эти RAII в плюсах. Вот дебилы, правда? че сложно написать delete ptr - где это нужно.
По моему, обертку писать не нужно вообще. Это ты придумал. Я предлагал юзать unique_ptr, если тебе хочется сделать вот именно так, а никак иначе.

Цитата applegame @
Очередной ошибочный вывод. Нет не только в исключениях. Также разница в ранних return, в том что код освобождения ресурса пишется прямо рядом с его захватом (прямо как твой любимый unique_ptr с кастомным деструктром, только гораздо опрятнее), ну и если таких мест несколько, то освобождение ресурсов будет выполнено автоматически в порядке обратном захвату. В сяшке ничего этого нет.

В Сяшке ровно тот же механизм, только вид с боку. Когда ты юзаешь мой любимый unique_ptr, то это полноценная RAII обертка, и она лишена практически всех недостатков. Например что будет у тебя, если во время выделения ресурса произойдет исключение? Будет очищать ресурс, который не существует?

Цитата applegame @
Ну так и пример багнутый. В D так не пишут. Чувак один ресурс обернул в scope(exit) а воторой доверил GC. Здорово!

Ну как не пишут, если пишут? Я ж даже ссылку привел на того, кто написал :-?

Цитата applegame @
А ты примерно так и сделал. Притащил какую-то неработающую херню со scope(exit) и сказал - оцтой какойто.

Ага, с тебя взял пример. Ты же первый начал рассказывать как без него тяжко в плюсах жить :D

Цитата applegame @
Неверно, scope(...) введен в язык для автоматического вызова кода при выходе из скоупа. А при работе с GC ресурсы обычно освобождаются самим GC. Накой там scope(...)?

На той же на кой finally в языках с GC существует. Расскажи, как ты закроешь сетевое соединение например по выходу из функции, если объект создается на куче? Да ты же бля напишешь scope(exit), ты уже приводил даже этот пример с PQExec :D

Цитата applegame @
И где тут костыль, даже если это написано на плюсах?

Понятия не имею по этому коду ничего не очевидно.

Цитата applegame @
Тебя трудно понять, Киля. Ты применяешь какие-то определения, выдавая их за общепринятые и потом тебя не понимают. Видимо ты имеешь в виду конструкцию, которую в C# называют try-catch-finally. Ну так ты опять ошибся, так как finally вполне можно написать без try-catch, а именно try-finally без catch.

Ты как раз таки все понял, просто тебе видимо потроллить хочеца. Я слышал в Java его частенько называют именно так. И название идет от полной конструкции. Finally не фигурирует сам по себе. Так что ты тоже ошибся выходит, если цеплятся к словам, как ты любишь ;)

Цитата applegame @
Ты привел пример, в котором RAII вполне уместен. Но я таки напишу специально для тебя все это дело в функциональном стиле без всякого RAII, но со scope(exit), дабы, так сказать, расширить твой и не только твой кругозор

Угумс, я уже видел похожее на шарпах, когда двухсотстрочная портянка оформлена в подобном виде! Очень удобно :good:
Там это к слову очень сильно распространено. Делегаты называется.

Автор: korvin 18.07.20, 14:51
Цитата applegame @
А при работе с GC ресурсы обычно освобождаются самим GC.

Обычно — нет. А финализаторы в Java давно признаны слишком проблемными. Поэтому ресурсы нужно использовать с try-with-resources.

Автор: D_KEY 18.07.20, 16:01
Цитата applegame @
Конечно. RAII - это прекрасная технология, особенно если нужен именно объект владеющий ресурсом.

А ты разве не такой пример привел?

Цитата
А вот в случае когда нужен скоуп владеющий ресурсом, RAII зачастую совершенно излишен.

Не согласен, он все равно лучше, чем явный defer/scope(exit).
Классический пример скоупа - блокировка.
Сравни:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    std::lock_guard lock(mtx);

И
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    mtx.lock();
    scope(exit) mtx.unlock();


Цитата
Угадай, как функциональщики без всяких RAII управляют, допустим, транзакциями в БД? :)

Транзакции не делал, а для управления ресурсами используется bracket pattern в том же haskell:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    main = do    
        withFile "foo.txt" ReadMode (\handle -> do  
            ...)


И это как раз то, о чем я говорю.

Цитата
В таком случае вероятность забывания написания scope равна вероятности забывания написания деструктора. :)

Верно. И к этому примеру никаких претензий, это аналог RAII, хотя и с ограничениями.
Но ты привел не этот код ;)

Именно так сделано в Ruby и Kotlin, например. Да и bracket pattern о том же, по сути.

Добавлено
Цитата applegame @
Но я привожу примеры, где scope(exit) проще и понятнее и не менее безопасно, чем RAII.

Пока у тебя не получилось :)

Цитата
scope это скорее про try/catch/finally.

Можно согласиться, что defer/scope(exit) лучше finally(хотя и не сильно).
Но еще лучше using/with/try-with-resources. Тем, что не нужно каждый раз указывать действие по освобождению.
На том же уровне with*-функции. ИМХО, их сложнее "забыть" использовать плюс они реализуются без изменения языка(как и RAII), что тоже плюс.
Но RAII выигрывает и у них. Хотя бы тем, что может быть точно так же использован для полей структуры/класса.

Автор: Qraizer 18.07.20, 16:39
Цитата Wound @
В среднем fread быстрее.
Я бы не стал доверять программе, написанной в нарушение Стандарта и неверно считающей оверхед. Совсем не верно. Вообще.

Автор: Qraizer 18.07.20, 17:48
Цитата JoeUser @
А что не так с Delphi?
Дык он тоже весь на finally. Там практически ничего без SEH написать невозможно, течь будет из всех щелей контрактов. Посмотри на нормальные Плюсовые сырцы: catch редки, а если и встречаются, то внутри реализаций.
Цитата applegame @
А зачем так писать? Заворачиваем в функцию и вызываем сколько угодно раз не парясь об освобождении ресурса
И чем это будет отличаться от классического RAII? Ты ещё не забыл о главном преимуществе scope()ов, за которое радеешь?
Цитата applegame @
А вот в случае когда нужен скоуп владеющий ресурсом, RAII зачастую совершенно излишен.
Вот. Собственно это и есть тот критерий, который оправдано можно закостылить scope()ом... но вдруг и внезапно – скоуп может захотеть владеть разными ресурсами, в разном количестве и с разной стратегией владения. Ну и получите и распишитесь, получили такой маленький упсик. Собственно, всё равно владельцем выступает программист, а скоуп просто выступает в роли послушного исполнителя твоей воли. Ибо дашь ему неверные инструкции, он и исполнит их неверно, а давать приходится постоянно, пусть и одни и те же. В RAII инструкции даются один раз, и они всегда одинаковые. Может быть иногда и не удобно, но всегда надёжно.

Добавлено
Цитата applegame @
Ну так в моем случае оно тоже автоматическое.
Не путай с автоматическим контролем и исполнением инвариантов. Исполнение данных тобой инструкций – да, автоматическое. С этим и __attribute__((__cleanup__())) справляется. Интересно, почему его не включали в Стандарт C? Это же так удобно, по твоим словам.

Автор: Qraizer 18.07.20, 18:16
Цитата Qraizer @
Я бы не стал доверять программе, написанной в нарушение Стандарта и неверно считающей оверхед. Совсем не верно. Вообще.
Чтобы далеко не посылать.
Первые два load-а читают посимвольно из-за того, что не знают, сколько там данных. Это также вызывает постоянные реаллокации приёмного буфера. Вторые два load-а читают скопом весь файл, заранее сконфигурировав приёмный буфер на требуемый размер. Что-то это мне напоминает... Ах да, std::vector<T> vs T*. Ну разве не прелесть.

Добавлено
Быстренько налобал правильный тест. Теперь все load-ы читают посимвольно в буфер достаточного размера. Полюбуйтесь:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    File test1.fil
    C++ istreambuf_iterator       : ..........       6906 us
    C++ stream::rdbuf             : ..........       7737 us (0.89)
    libc fread                    : ..........      33049 us (0.21)
    POSIX read                    : ..........    2367907 us (0.00)
    File test2.fil
    C++ istreambuf_iterator       : ..........      13458 us
    C++ stream::rdbuf             : ..........      15141 us (0.89)
    libc fread                    : ..........      64100 us (0.21)
    POSIX read                    : ..........    4671184 us (0.00)
    File test4.fil
    C++ istreambuf_iterator       : ..........      29175 us
    C++ stream::rdbuf             : ..........      32303 us (0.90)
    libc fread                    : ..........     133641 us (0.22)
    POSIX read                    : ..........    9758374 us (0.00)
    File test8.fil
    C++ istreambuf_iterator       : ..........      60140 us
    C++ stream::rdbuf             : ..........      65874 us (0.91)
    libc fread                    : ..........     267079 us (0.23)
    POSIX read                    : ..........   19191693 us (0.00)
    File test16.fil
    C++ istreambuf_iterator       : ..........     117328 us
    C++ stream::rdbuf             : ..........     130321 us (0.90)
    libc fread                    : ..........     547789 us (0.21)
    POSIX read                    : ..........   38444935 us (0.00)
    File test32.fil
    C++ istreambuf_iterator       : ..........     243340 us
    C++ stream::rdbuf             : ..........     265980 us (0.91)
    libc fread                    : ..........    1090477 us (0.22)
    POSIX read                    : ..........   75824446 us (0.00)

Автор: JoeUser 18.07.20, 18:31
Цитата Qraizer @
Чтобы далеко не посылать.
Первые два load-а читают посимвольно из-за того, что не знают, сколько там данных. Это также вызывает постоянные реаллокации приёмного буфера. Вторые два load-а читают скопом весь файл, заранее сконфигурировав приёмный буфер на требуемый размер. Что-то это мне напоминает... Ах да, std::vector<T> vs T*. Ну разве не прелесть.

Да, надо свое мутить! :lol: Хотя есть задачи и по важнее. Например "окроссплатформить" чтение/запись в файл по позиционированию по off_t для x32/64.

Автор: Qraizer 18.07.20, 18:35
Ну и это я ещё спустил на тормозах const_cast<char*>(std::string::data())

Автор: applegame 19.07.20, 06:15
Цитата Wound @
Он как раз все верно оценил, если я правильно понял что там произошло.
Если бы он все верно оценил, он бы так не написал. Очевидно, что он думал, что деструкторы объектов на куче вызываются по тем же правилам, что и деструкторы объектов на стеке.
Цитата Wound @
Ну да, нахер юзать все эти RAII в плюсах. Вот дебилы, правда? че сложно написать delete ptr - где это нужно.
Ну так они и пишут, в деструкторах оберток и кастомных делетерах unique_ptr.
Цитата Wound @
В Сяшке ровно тот же механизм, только вид с боку. Когда ты юзаешь мой любимый unique_ptr, то это полноценная RAII обертка, и она лишена практически всех недостатков. Например что будет у тебя, если во время выделения ресурса произойдет исключение? Будет очищать ресурс, который не существует?
Нет в сяшке не тот же. Вызовуться только те scope(), которые успели отработать. Поведение scope(exit) в плане очистки ресурсов полностью совпадает с unique_ptr с кастомным деструктором. Похоже, ты не совсем понял оно работает. Вот посмотри очень короткий пример: https://run.dlang.io/is/7jJE7b
Цитата Wound @
Ну как не пишут, если пишут? Я ж даже ссылку привел на того, кто написал
Он начинающий, очевидно. Представь себе, что новичок, заюзал shared_ptr для некоего обекта. Потом зачем-то в деструкторе какой-нибудь RAII-обертки сделал TTF_Quit, а позже в деструкторе объекта торчащем в shared_ptr вызвался TTF_CloseFont. Сегфолт. Похожим образом я много лет назад облажался с boost::asio. Объект сидящий в boost::shared_ptr (в те времена, еще не было std::shared_ptr) пережил другой объект, без которого он не мог нормально работать. Долго не мог понять что за говно, потом понял и больше так не делал. Ибо нефиг.
Цитата Wound @
сам по себе scope(exit) - Не влияет на порядок вызовов деструкторов, я нигде не утверждал что он влияет.
Ты написал так:
Цитата Wound @
Ну и к слову, встречал что если класс в D создавать через new, то порядок вызовов с этим scope(exit) - уже не такой однозначный, а все из за GC, так что даже эта "плюшка" может запросто поломать программу.
Я это понял так, что ты решил, что scope(exit) как-то влияет на это.
Цитата Wound @
Ага, с тебя взял пример. Ты же первый начал рассказывать как без него тяжко в плюсах жить
Ну я не тащил сюда всякие странные багнутые куски кода. А так да, в C++ нет scope, но есть RAII, а в D есть и scope и RAII. :)
Цитата Wound @
Ты как раз таки все понял, просто тебе видимо потроллить хочеца. Я слышал в Java его частенько называют именно так. И название идет от полной конструкции. Finally не фигурирует сам по себе. Так что ты тоже ошибся выходит, если цеплятся к словам, как ты любишь
Я не сразу понял. Когда я писал try/catch/finally я имел в виду совокупность всех возможных комбинаций и так же понял и твое try/catch/finally.
Важно понимать, что scope(success)/scope(failure)/scope(exit) похожи на try/catch/finally, но отличия достаточно большие.

Автор: applegame 19.07.20, 07:11
Цитата korvin @
Обычно — нет. А финализаторы в Java давно признаны слишком проблемными. Поэтому ресурсы нужно использовать с try-with-resources.
Да, наверное ты прав, обычно нет. Та же фигня и с деструкторами классов в D.
Цитата D_KEY @
А ты разве не такой пример привел?
Нет не такой. Мне не нужен был объект владеющий ресурсом, мне нужен был сам ресурс. Под объектом в данном случае я подразумеваю не просто владение, но и предоставление интерфейса для работы с этим ресурсом. В моем случае, интерфейс был реализован через сишные вызовы внешних функций и, я не вижу никакого смысла писать обертку даже просто для владения. Результат запроса (указатель на PGresult), возвращаемый из сишной либы, никуда за пределы одной-единственной функции не попадает. Понимаешь? Этот PQClear встречается во всей моей обертке вокруг libpq ровно один раз, а именно в scope(exit) в одном из методов класса Connection. Ты предлагаешь городить RAII-обертку на случай: "а вдрух когда-нибудь мне понадобится работать с PGresult в нескольких разных местах"? Вот когда понадобится тогда и напишу, но скорее не RAII, а в функциональном стиле, так как считаю что писать RAII исключительно для владения - костыль. Но пока не понадобилось, и я более чем уверен что и не понадобится.
Цитата D_KEY @
Не согласен, он все равно лучше, чем явный defer/scope(exit).
Классический пример скоупа - блокировка.
Тут у тебя неверное сравнение. Ты же скрыл код самого std::lock_guard, в котором присутствует такой же mtx.unlock(). Тогда двай уж и scope(exit) вынесем в отдельную функцию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    {
      std::lock_guard lock(mtx);
      ...
    }

против
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      locked(mtx, {
      ...
      });

И знаешь чем второе лучше первого? Нет бесполезного и странно выглядящего для людей не знающих C++ объявления переменной lock, которая нигде потом явно не используется. То самое создание объекта исключительно ради владения ресурсом и без предоставления интерфейса работы с этим ресурсом. Самый что ни на есть настоящий костыль.
Цитата D_KEY @
Верно. И к этому примеру никаких претензий, это аналог RAII, хотя и с ограничениями.
Но ты привел не этот код
Вообще-то именно такой код я и привел. Более того, только в подобных случаях и уместен scope(exit). Если тебе приходится писать scope(exit) с одним и тем же кодом более одного раза - ты что-то делаешь не так.
Цитата D_KEY @
Можно согласиться, что defer/scope(exit) лучше finally(хотя и не сильно).
Не знаю что за defer (Go?). А вот scope(...) довольно сильно отличается от finally как синтаксисом так и семантикой.
Цитата D_KEY @
Но еще лучше using/with/try-with-resources. Тем, что не нужно каждый раз указывать действие по освобождению.
Ты просто читаешь тему по диагонали :) Я с самого начала говорил и несколько раз потом повторил, что scope(...) удобен для однократного использования и он умеет работать с чем угодно, а не только с ресурсами. Даже примеры счетчиков запросов приводил. А ты зациклился на ресурсах и DRY. Давай расциклись уже :D
Если тебе нужно работать с ресурсом в разных местах, заверни scope(...) в отдельную функцию (получится тот же using/with/try-with-resources, только лучше, потому что гибче) или делай RAII.
Цитата D_KEY @
Но RAII выигрывает и у них. Хотя бы тем, что может быть точно так же использован для полей структуры/класса.
Это как сказать, что вездеход лучше автомобиля, потому что вездеход может быть точно также использован для езды по болотам. :lol:

Автор: korvin 19.07.20, 07:47
Цитата applegame @
Не знаю что за defer (Go?)

Да

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    f, err := os.Open("some/file")
    if err != nil { return err }
    defer f.Close()


и ещё Zig, например

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    const std = @import("std");
     
    pub fn main() !void {
        const file = try std.fs.cwd().openFile("does_not_exist/foo.txt", .{});
        defer file.close();
        try file.writeAll("all your codebase are belong to us\n");
    }

Автор: applegame 19.07.20, 08:32
Цитата Qraizer @
И чем это будет отличаться от классического RAII? Ты ещё не забыл о главном преимуществе scope()ов, за которое радеешь?
Тем, что написать такую обертку проще, чем RAII-класс. Ну и в случаях вроде лока мютексов еще и нет бесполезных объявлений переменных. Посмотри на свою иммитацию scope: scope1, scope2, scope3... - что это за мерзкие нумерованные переменные, которые нигде не задействованы? Вот где костыль-то.
Цитата Qraizer @
Вот. Собственно это и есть тот критерий, который оправдано можно закостылить scope()ом...
Ага, "костыль", который выглядит чище, а работает не хуже. :D Костыль - это стрельба из пушки по воробьям.
Цитата Qraizer @
но вдруг и внезапно – скоуп может захотеть владеть разными ресурсами, в разном количестве и с разной стратегией владения.
Если скоуп внезапно захочет чего-то сложного, то внезапно убирается scope(...) и делается та самая RAII-обертка. Но на моей практике такого никогда не было. Если я принял решение использовать scope(...), то в будущем так и не возникала необходимость его менять на RAII.
Цитата Qraizer @
Может быть иногда и не удобно, но всегда надёжно.
Это называется оверинжиниринг, когда код усложняется "на всякий случай", даже когда вероятность возниконовения этого "всякого случая" в будущем близка к нулю. Я сам страдаю таким и раньше мне приходилось местами заставлять себя писать scope(exit) вместо RAII-обертки. Потому что объективная оценка показывала, что в данном случае "вдруг и внезапно скоуп никогда не захочет владеть разными ресурсами, в разном количестве и с разной стратегией владения." Сейчас уже почти привык не городить абстракции и обобщения там, где они вероятно не понадобятся никогда.
Цитата Qraizer @
Исполнение данных тобой инструкций – да, автоматическое. С этим и __attribute__((__cleanup__())) справляется. Интересно, почему его не включали в Стандарт C? Это же так удобно, по твоим словам.
А по твоему удобнее эти goto? Да и cleanup гораздо примитивнее scope(exit).
А так сишники народ странный, но думаю многим из них понравился бы scope(exit). А Стандарт это дело консервативное и тормозное. Сколько ждали появления в плюсах лямбд и auto?

Добавлено
Цитата korvin @
и ещё Zig, например
Да, вспомнил. Я пытался юзать Zig, но как-то не зашло. Слишком уж странные у них лозунги. ЕНМНИП, у них до сих пор нет лямбд.

Автор: korvin 19.07.20, 08:59
Цитата applegame @
ЕНМНИП, у них до сих пор нет лямбд.

Так это приукрашенный C, откуда там лямбды? =)

Автор: applegame 19.07.20, 09:34
Цитата korvin @
Так это приукрашенный C, откуда там лямбды? =)
Они там полиморфизьм обсуждают :) Почему бы и лямбды не сделать? Они вроде не противоречит их девизам.

Автор: D_KEY 19.07.20, 10:02
Цитата applegame @
Этот PQClear встречается во всей моей обертке вокруг libpq ровно один раз, а именно в scope(exit) в одном из методов класса Connection.

Это было не очевидно из твоего кода. Если так, то спорить тут не о чем.

Цитата
Тогда двай уж и scope(exit) вынесем в отдельную функцию

Если мы это сделаем, то это будет уже другой подход. Понимаешь? :)

Цитата
И знаешь чем второе лучше первого?

Я достаточно подробно описал твое "второе" в предыдущем ответе тебе. И даже сказал, что не вижу в этом подходе ничего плохого, просто RAII более универсальный подход.

Цитата
Нет бесполезного и странно выглядящего для людей не знающих C++ объявления переменной lock, которая нигде потом явно не используется.

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

Зависит от того, к чему привык конкретный программист, который будет читать код. Вкусовщина.

Цитата
Самый что ни на есть настоящий костыль.

Я тебя уже просил привести свое определение термина "костыль". Никакой принципиальной разницы тут нет, поскольку и в случая RAII и в случае bracket pattern ты освободишь ресурс автоматически. В первом случае за счет "лишнего" объекта, во втором - за счет "лишних" вызова функции и лямбды.

Цитата
заверни scope(...) в отдельную функцию (получится тот же using/with/try-with-resources, только лучше, потому что гибче) или делай RAII.

Если ты используешь scope(exit) просто в реализации bracket pattern, то для работы с ресурсом ты используешь bracket pattern, а не scope(exit), понимаешь? scope(exit) у тебя просто часть реализации, там вооще мог быть try...catch, это не имеет принципиального значения ;)
Впрочем, я писал уже, что scope(exit) лучше finally, хотя и не принципиально.

Цитата
Цитата D_KEY @
Но RAII выигрывает и у них. Хотя бы тем, что может быть точно так же использован для полей структуры/класса.
Это как сказать, что вездеход лучше автомобиля, потому что вездеход может быть точно также использован для езды по болотам. :lol:

У автомобиля есть преимущества перед вездеходом, у bracket pattern перед RAII можешь такие указать?

Добавлено
Цитата korvin @
Цитата applegame @
Не знаю что за defer (Go?)

Да

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    f, err := os.Open("some/file")
    if err != nil { return err }
    defer f.Close()

Там, кстати, не собираются делать аналог try-with-resources? А with*-функции используют?

Автор: applegame 19.07.20, 11:07
Цитата D_KEY @
Это было не очевидно из твоего кода. Если так, то спорить тут не о чем.
Я словами это говорил:
Цитата applegame @
Это освобождение в моей обертке фигурировало буквально внутри одной функции.
И потом еще неоднократно повторял. Но ты походу писатель а не читатель :) Впрочем, я тебя понимаю, кому охота читать портянки бессодержательных споров с Килей. :lol:
Цитата D_KEY @
Если мы это сделаем, то это будет уже другой подход. Понимаешь?
Конечно. Но ты же сам придумал это сравнение? Изначально-то речь шла о ситуации, когда у тебя выбор самому писать одноразовую RAII-обертку или воспользоваться одноразовым же scope(exit). А тут ты приводишь пример готовой RAII-обертки из стандартной либы да и еще подразумевая многократное ее применение.
Цитата D_KEY @
Зато есть странно выглядищий для людей не знакомым с паттерном вызов функции с передачей лямбды, хотя логично делать эти действия на том же уровне, поскольку контекст тот же и мы не специфицируем никакое поведение.
Лямбда подхватывает текущий контекст. Кроме того лямбда вообще может выглядеть просто как скоуп, который лочится. Например в D:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    mutex.locked({
       ...
    });
Лично мне это видится более естественным, чем объявление переменной-гварда. Вызов функции любому программисту понятен: внутри что-то делается, по названию функции можно понять что именно делается. А в случае с объявлением переменной-гварда, нужно знать, что это RAII-объект. Потому что переменная объявлена и ничего с ней больше не происходит. Неплюсовик ожидает, что если переменную объявили, то значит ее собираются использовать, если же ее не используют, то это скорее всего ошибка (даже ворнинг такой есть unused variable). То бишь, в данном случае объявление переменной используют не по общепринятому назначению. Вот это я и называю костылем.
Цитата D_KEY @
Я тебя уже просил привести свое определение термина "костыль".
Нет, сначала ты приведи свое определение термина "определение термина". :)
Все субъективно, я привел свое мнение. А так вон фанаты сяшки или, прости господи, Zig будут называть любой твой RAII - вселенским злом и страшным костылем. И аргументы у них найдутся из серии: в этом вашем RAII-объекте непонятно что происходит, а в моей милой функции все явно написано. И хрен ты их переубедишь :)
Я высказал свое мнение и написал, что именно считаю костылем. Тебе это не показалось костылем - ну и флаг тебе в руки, что еще ты от меня хочешь? :D
Мы же тут просто мнениями обмениваемся и не ставим цели учить писать программы.
Цитата D_KEY @
Если ты используешь scope(exit) просто в реализации bracket pattern, то для работы с ресурсом ты используешь bracket pattern, а не scope(exit), понимаешь? scope(exit) у тебя просто часть реализации, там вооще мог быть try...catch, это не имеет принципиального значения
Впрочем, я писал уже, что scope(exit) лучше finally, хотя и не принципиально.
Да-да, так и есть. И scope(exit) очень удобно применять для реализации этого, хм паттерна, в языках, в которых существют исключения и ранний return.
До этого обсуждения ни разу не слышал, что это паттерн и что у него, оказывается, даже есть название. Хотя это похоже какой-то чисто хаскелевский мемасик.

Автор: applegame 19.07.20, 11:24
Расслабтесь:
io_thief.jpg (, : 151)

Автор: korvin 19.07.20, 11:36
Цитата D_KEY @
Там, кстати, не собираются делать аналог try-with-resources?

Нет, и не думаю, что станут: try-with-resources работает только в рамках вызова процедуры и одного потока (одной горутины) — слишком узкоспециализированный механизм. Кроме того, try-with-resources в Java работает только если класс реализует
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    interface AutoCloseable {
     
        void close() throws Exception
    }

а разные «ресурсы» могут иметь разные методы «захвата/освобождения» в соответствии с их семантикой: Open/Close, Lock/Unlock, Create/Delete и т.п. Либо заставлять всех использовать всегда один метод (Close, там или Release) — как-то тупо. Вводить в язык явное понятие ресурса с каким-нибудь спец-синтаксисом, типа помечать освобождающий метод каким-нибудь ключевым словом или спец-символом — ну хз. И это всё равно никак не поможет с разделяемыми ресурсами, например:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    package main
     
    import (
        "fmt"
        "sync"
        "time"
    )
     
    const nWorkers = 5
     
    func main() {
        fmt.Println("ok1:")
        ok1()
        fmt.Println("\nok2:")
        ok2()
        fmt.Println("\nnot ok:")
        notOk()
    }
     
    func ok1() {
        var wg sync.WaitGroup
        wg.Add(nWorkers) // acquire
        for i := 0; i < nWorkers; i++ {
            go func(x int) {
                defer wg.Done() // release
                doSomeWork(x)
            }(i)
        }
        wg.Wait()
    }
     
    func ok2() {
        var wg sync.WaitGroup
        for i := 0; i < nWorkers; i++ {
            wg.Add(1) // acquire
            go func(x int) {
                defer wg.Done() // release
                doSomeWork(x)
            }(i)
        }
        wg.Wait()
    }
     
    func notOk() {
        var wg sync.WaitGroup
        for i := 0; i < nWorkers; i++ {
            go func(x int) {
                wg.Add(1) // acquire
                defer wg.Done() // release
                doSomeWork(x)
            }(i)
        }
        wg.Wait()
    }
     
    func doSomeWork(x int) {
        <-time.After(1 * time.Second)
        fmt.Println("done #", x)
    }

=>
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ok1:
    done # 2
    done # 0
    done # 4
    done # 3
    done # 1
     
    ok2:
    done # 0
    done # 2
    done # 1
    done # 3
    done # 4
     
    not ok:
     
    Program exited.


try-with-resources тут никак не поможет, т.к. позволяет описать только вариант notOk.

А в JDK, ЕМНИП, у HttpURLConnection освобождающий метод называется disconnect, а не close и соответствено класс не реализует AutoCloseable и использовать его в try-with-resources нельзя :facepalm:

Цитата D_KEY @
А with*-функции используют?

Ну, некоторые используют, но callback-style не всем нравится.

Автор: Wound 19.07.20, 12:31
Цитата applegame @
Если бы он все верно оценил, он бы так не написал. Очевидно, что он думал, что деструкторы объектов на куче вызываются по тем же правилам, что и деструкторы объектов на стеке.

Это тебе очевидно и синтетического примера в 5 строчек. Если бы все было просто как ты пишешь, баги делали бы только студенты по своему не знанию. На практике у него может быть довольно не примитивный код, чтоб верно оценить все ньюансы. Именно по этому писать руками дополнительную строчку - может являтся источником ошибок.
Ок, вот ты привел там функцию - с виду имитирующуюю RAII, в принципе такой вариант еще ладно, если знаешь что у тебя тут голый ресурс, и ты тут его же удаляешь - такое еще можно оправдать. Но ведь разные есть случаи, я понял бы если бы это была единная конструкция, хотя бы там через точку например. Но понимаешь ли, бывает так что пишешь new int, и тут же забываешь сделать delete, потому что это две конструкции, независимы друг от друга семантически. Так и тут.

Цитата applegame @
Ну так они и пишут, в деструкторах оберток и кастомных делетерах unique_ptr.

Это пишется в одной конструкции. Если было бы все так просто - все эти RAII нахер бы никому не упали. Какая проблема вызвать delete, после выделения ресурсов - да абсолютно никакой. И тем не менее в том же С++ утечек куча, из за того что люди полагаются на свои руки и память, а потом выхватывают утечки.

Цитата applegame @
Нет в сяшке не тот же. Вызовуться только те scope(), которые успели отработать. Поведение scope(exit) в плане очистки ресурсов полностью совпадает с unique_ptr с кастомным деструктором. Похоже, ты не совсем понял оно работает. Вот посмотри очень короткий пример: https://run.dlang.io/is/7jJE7b

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

Цитата applegame @
Я это понял так, что ты решил, что scope(exit) как-то влияет на это.

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

Цитата applegame @
Ну я не тащил сюда всякие странные багнутые куски кода. А так да, в C++ нет scope, но есть RAII, а в D есть и scope и RAII.

Я его притащил - показать с какими проблемами можно столкнуться, полностью положившись на него. В С++ нет scope, потому что там нет GC, а в D есть GC, поэтому там и scope актуален. Для С++ этот scope(exit) - не более, чем синтаксический сахар.

Цитата applegame @
Я не сразу понял. Когда я писал try/catch/finally я имел в виду совокупность всех возможных комбинаций и так же понял и твое try/catch/finally.
Важно понимать, что scope(success)/scope(failure)/scope(exit) похожи на try/catch/finally, но отличия достаточно большие.

По сути все отличия сводятся к удобству использования. Не надо просто писать все эти try/catch/finally, но например тотже try-with-resources/using - уже куда более удобны этих конструкций try/catch/finally, и вполне себе могут даже потягаться со scope(exit).
Да и в такие языки любят тащить все попало, но scope(exit) - я там как то не встречал. Видимо даже там разрабы посчитали - что излишне вводить то, что уже есть в языке.

Автор: applegame 19.07.20, 13:38
Цитата Wound @
Если бы все было просто как ты пишешь, баги делали бы только студенты по своему не знанию.
Такие ошибки и делают только студенты по своему незнанию.
Цитата Wound @
Ок, вот ты привел там функцию - с виду имитирующуюю RAII, в принципе такой вариант еще ладно, если знаешь что у тебя тут голый ресурс, и ты тут его же удаляешь - такое еще можно оправдать.
Ну так scope(...) и надо применять только в таких случаях. Я же говорил много раз.
Цитата Wound @
Но ведь разные есть случаи, я понял бы если бы это была единная конструкция, хотя бы там через точку например. Но понимаешь ли, бывает так что пишешь new int, и тут же забываешь сделать delete, потому что это две конструкции, независимы друг от друга семантически. Так и тут.
Я хз как такое можно забыть. Ты же вот прямо только что выделил память, и если ты не страдаешь склерозом, в голове автоматом возникает мысль: "надо освободить ресурс", и ты тут же пишешь scope(exit). С таким же успехом ты можешь забыть написать кастомный делетер в unique_ptr. Вот если у тебя такое встречается много раз, то уже становится неудобно, но в таких случаях и unique_ptr с кастомным делетером становится неудобным.
Цитата Wound @
Тут как бы и коню понятно что до второго scope оно не дойдет. Корректно ли отработает первый scope, если исключение произошло на этапе конструирования сложного объекта, вот в чем вопрос.
Конечно корректно, ведь конструирование происходит до scope(exit), соотвественно, если при конструировании произошло исключение, то scope(exit) не отработает. Разве это не очевидно?
Цитата Wound @
Имелось ввиду, что ты можешь положится на scope, ну раз оно от RAII вообще никак не отличается по твоим словам, а гдето выше другой чувак понадеется на полноценный деструктор, и это приведет к ошибке, как в примере по ссылке.
Он положился на scope и scope сделал ровно то, что он и ожидал, не больше не меньше. Его "обманул" GC, а не scope.
Цитата Wound @
Я его притащил - показать с какими проблемами можно столкнуться, полностью положившись на него.
Наоборот, он столкнулся с проблемой, потому что не полностью положился на scope, а понадеялся на деструктор. Если бы он полностью положился на scope и написал бы в нем TTF_CloseFont() и TTF_Quit(), то все бы работало без сегфолтов.
Цитата Wound @
В С++ нет scope, потому что там нет GC, а в D есть GC, поэтому там и scope актуален. Для С++ этот scope(exit) - не более, чем синтаксический сахар.
Неверно. scope(exit) и GC - вещи ортогональные. В моем же примере с PQClean, GC вообще никак не используется. И повторю еще раз: scope(...) может работать не только с ресурсами (в частности не только с памятью). И также повторю, что все RAII-парадигмы C++ работают и в D. Ну и какой это сахар, если для его реализации в плюсах надо маленькую либку написать, что и сделал Qraizer.
Цитата Wound @
но например тотже try-with-resources/using
Это сущности чуть более высокого уровня, чем scope(exit), но их можно реализовать при помощи scope(exit).
Цитата Wound @
Да и в такие языки любят тащить все попало, но scope(exit) - я там как то не встречал. Видимо даже там разрабы посчитали - что излишне вводить то, что уже есть в языке.
Разрабы тащат в языки все что им в голову взбредет, сам факт что разрабы что-то притащили или не притащили вообще не критерий. Тащат говно и наоборот хорошее не тащат сплошь и рядом.

Автор: Wound 19.07.20, 14:15
Цитата applegame @
Такие ошибки и делают только студенты по своему незнанию.

Ну да, а остальные не делают никаких ошибок. :good: Я уже где то такое слышал.

Цитата applegame @
Ну так scope(...) и надо применять только в таких случаях. Я же говорил много раз.

Ну вообще мы объясняли человеку почему лучше в его случае избавится от goto, и обернуть ресурс в RAII обертку, приводили разные примеры, пришол ты и сказал - как хорошо что в D такого говна нет, а есть scope(exit). А теперь ты говоришь - что не говорил что он весь такой из себя ахереть какой.

Цитата applegame @
Я хз как такое можно забыть. Ты же вот прямо только что выделил память, и если ты не страдаешь склерозом, в голове автоматом возникает мысль: "надо освободить ресурс", и ты тут же пишешь scope(exit). С таким же успехом ты можешь забыть написать кастомный делетер в unique_ptr. Вот если у тебя такое встречается много раз, то уже становится неудобно, но в таких случаях и unique_ptr с кастомным делетером становится неудобным.

Когда работаешь с сырым указателем - очень часто забываешь написать delete. Тут примерно тоже самое - потому что это отдельная конструкция. С кастомным делитером - сразу видно будет что ресурс не освобождается, просто по самой конструкции. А этот scope(exit) - не обязательно можно написать сразу после выделения, его можно написать и в конце и в середине кода, уже после выделения памяти под ресурс. А раз так написать можно - значит будь уверен кто нибудь непременно так и напишет. Весь вопрос - как долго будут потом багу искать. Может быть сразу найдут, а может кто то неделю в отладчике проведет. Я вот именно это имею ввиду. Да ты можешь написать - вызывай сразу после выделения памяти под ресурс, но от этого возможность написать его где угодно никуда не девается.

Цитата applegame @
Конечно корректно, ведь конструирование происходит до scope(exit), соотвественно, если при конструировании произошло исключение, то scope(exit) не отработает. Разве это не очевидно?

Ну например там:
Цитата

Установили соединение
Открыли базу
Начали получать доступ к таблице - исключение

scope(exit){
Очистили ресурсы от таблицы
Закрыли Базу
Закрыли соединение
}

В итоге соединение и БД остались открытыми.

Цитата applegame @
Он положился на scope и scope сделал ровно то, что он и ожидал, не больше не меньше. Его "обманул" GC, а не scope.

Мы не знаем кто там кого обманул, вполне возможно что он написал все верно, а потом пришел вася, и вместо создания объекта на стеке как было, взял переделал создание объекта на куче.
Еще раз повторяю - там синтетический пример, обычно такие пишут, чтоб не захламлять лишними деталями и не сбивать людей с толку. Вот допустим у меня проект, хренова туча классов/кода летит какая то ошибка, я не буду все это выкладывать на форум, чтоб спросить что не так. Я возьму напишу минимальный, синтетический пример для воспроизведения проблемы, и выкачу его на форум, там будет 2 класса и три строчки с выводом в консоль, а в реале, может быть сотни классов и строк кода.

Цитата applegame @
Неверно. scope(exit) и GC - вещи ортогональные. В моем же примере с PQClean, GC вообще никак не используется. И повторю еще раз: scope(...) может работать не только с ресурсами (в частности не только с памятью). И также повторю, что все парадигмы C++, в том числе и RAII работают и в D. Ну и какой это сахар, если для его реализации в плюсах надо маленькую либку написать, что и сделал Qraizer.

Ну да, scope ортогонален GC примерно на столько же, на сколько и finally. Для его реализации достаточно написать unique_ptr, можно и класс написать. Qraizer тебе писал аналог в D, чтоб оно и выглядело и юзалось прям как один в один, максимально приближенно. Т.е. по сути он тебе этот сахар и реализовал на плюсах. А так это ничем не отличается от любого класса, в деструкторе которого выполняется то, что у тебя выполняется в теле scope. Потому как оно ровно так же - гарантированно выполняется в конце блока, устойчиво к исключениям, по сути как и finally. Так что scope(exit) - в плюсах это синтаксический сахар, не более. Были бы лямбды в плюсах более лаконичными, хотя бы похожи на делегаты в шарпах, то можно было бы переписать код с std::unique_ptr с кастомным делитером примерно вот так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto resource = std::unique_ptr(new CResource(), (e) => {
      //! free resource.
      delete e;
    });

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

Ну и по описанию: https://tour.dlang.org/tour/en/gems/scope-guards
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        // scope guards allow placing allocations
        // and their clean up code next to each
        // other
        import core.stdc.stdlib : free, malloc;
        int* p = cast(int*) malloc(int.sizeof);
        scope(exit) free(p);

Приводят ровно такой пример, который в С++ переписывается обычным unique_ptr, в примере free вызывается в scope(exit), в плюсах будет вызываться в кастомном делитере.

Цитата applegame @
Разрабы тащат в языки все что им в голову взбредет, сам факт что разрабы что-то притащили или не притащили вообще не критерий. Тащат говно и наоборот хорошее не тащат сплошь и рядом.

Ну в такие ЯП как JAVА/C# по моему тащат вообще все подряд, что можно затащить.

Автор: Qraizer 19.07.20, 16:20
Цитата applegame @
Ага, "костыль", который выглядит чище, а работает не хуже.
Именно что выглядит. Так-то он хуже. Потому что не избавляет пользователей scope()-ов от того, чтобы быть постоянно быть во внимании и заниматься рутинным делом: проверять успех небезотказной операции и клепать код отмены транзакций. RAII требует этого только от автора.
Ну и опять же, если пользователей не больше авторов :D , тогда scope()-ы окупаются. Но в промышленных решениях это редкие события.

Добавлено
Цитата applegame @
Сколько ждали появления в плюсах лямбд и auto?
Объективно – пару лет. В бусте подобные штуки появились быстро, и в следующий Стандарт заехали без проблем.

Автор: JoeUser 19.07.20, 16:35
Цитата Qraizer @
Объективно – пару лет

Враки! :lol: Я с лямбдами в Perl'е работал с 1998 года (и то - я опаздун в освоении Перла). В сях - "не прошло и пол-года".

Автор: Qraizer 19.07.20, 16:58
Цитата applegame @
Посмотри на свою иммитацию scope: scope1, scope2, scope3... - что это за мерзкие нумерованные переменные, которые нигде не задействованы? Вот где костыль-то.
Угу. А если:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #define CAT(X, Y) X##Y
    #define VAR(X, Y) CAT(X, Y)
    #define SCOPE ScopeD::scope VAR(dummy, __LINE__)
то лучше? Ненуачё...
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int main()
    {
      using namespace ScopeD;
     
      SCOPE (failure, [](){ std::cout << "scope(exit) 1" << std::endl; });
      throw std::runtime_error("exception");
      SCOPE (failure, [](){ std::cout << "scope(exit) 2" << std::endl; });
    }

:unsure:

Автор: JoeUser 19.07.20, 17:24
Qraizer, как уж на сковородке! :lool: Но таки - аcилил?! )))

Добавлено
Qraizer, наверное можно было бы и всю лямбду в макро загнать?

Автор: Qraizer 19.07.20, 22:19
Цитата JoeUser @
Но таки - аcилил?! )))
Кому вдруг критично :-? , пусть юзают. Я так не считаю. И даже не думаю, что кому надо было бы, не написал бы сам молча, а запросил саппорт "памагите".
Цитата JoeUser @
наверное можно было бы и всю лямбду в макро загнать?
М-м, нет. В общем случае лямбда может быть очень сложной, препроцессор поломается на разделителях. Да и зачем? Препроцессор штука, конечно, мощная, но ну её.

Автор: JoeUser 20.07.20, 16:48
Qraizer, а вот тут
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // ...
    Guard sameAppGuarded([=]() { CloseHandle(sameApp); });
    // ...

Может в лямбде лучше по ссылке захватывать?

Добавлено
Дааа ... у этого RAII тоже есть побочки. Решил переписать "по-фэншую", и как оказалось, выплыла шляпа ;) Когда без RAII - критикуют мол, забудешь освободить ресурс. А как оказалось, в моей проге, пришлось в каждую лямбду вставить проверку на ошибку. Типа, если действительно была ошибка - освобождать, а если нет - пропустить. И только в одной лямбде это не потребовалось. Поэму рисуется контр-аргумент: не поставишь проверку необходимости освобождения - получишь ошибку времени исполнения.

Добавлено
ЗЫ: Qraizer, мне понравились твои гварды! Аккуратнее всего получается. :good:

Автор: D_KEY 20.07.20, 17:31
Цитата JoeUser @
А как оказалось, в моей проге, пришлось в каждую лямбду вставить проверку на ошибку. Типа, если действительно была ошибка - освобождать, а если нет - пропустить. И только в одной лямбде это не потребовалось. Поэму рисуется контр-аргумент: не поставишь проверку необходимости освобождения - получишь ошибку времени исполнения.

Возможно, ты что-то не так делаешь, например, не передаешь владение ресурсом. И о каких лямбдах речь?

Можешь код привести?

Автор: JoeUser 20.07.20, 17:43
Цитата D_KEY @
Можешь код привести?

Ну вот кусок:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // ...
    // инициализация библиотеки
    if (libssh2_init(0) != 0) {
      sftpError = sftp_error::init_error;
      return false;
    }
    Guard Guard_init([&]() {
      if (sftpError != sftp_error::no) libssh2_exit();
    });
    // поиск адреса хоста
    // ...
    // ...
    // ...

libssh2_exit() нужно вызывать только тогда, когда что-то потом пошло не так. Но если все отработало нормально - libssh2 должна остаться проинициализированной.

Автор: D_KEY 20.07.20, 17:56
Цитата JoeUser @
libssh2_exit() нужно вызывать только тогда, когда что-то потом пошло не так. Но если все отработало нормально - libssh2 должна остаться проинициализированной.

Остаться-то должна, но потом где-то же должен быть вызов libssh2_exit все равно. Вот туда и надо передать владение. А не через guard' ы.

Автор: JoeUser 20.07.20, 18:21
Цитата D_KEY @
Остаться-то должна, но потом где-то же должен быть вызов libssh2_exit все равно.

Это в деструкторе вызывается:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    sftp_class::~sftp_class() {
      if (sftpError != sftp_error::winsocket_error) {
        if (sftpState == sftp_state::logged_in) Logoff(); // ◄────── вот тут и вызовется libssh2_exit
          #ifdef WIN32
          WSACleanup();
          #endif
        }
      }
    }

Автор: D_KEY 20.07.20, 18:27
Ну вот в случае RAII у тебя и будет объект ssh_initializer или что-то вроде того. Который в деструкторе сделает ssh2_exit, если владеет объектом. Этот объект делаешь полем класса. Локально в функции, где идет init, ты создаешь временный объект этого класса и только в случае успеха передаешь владение полю.
Примерно так.

Автор: JoeUser 20.07.20, 18:47
Тогда нарушается логика поведения класса. В методе Login происходит полная инициализация подключение к серверу, ну и подключение. Если логин не прошел - экземпляр класс болтается с состоянием ошибки, по не будет удален (ну или еще попытки логина). В деструкторе вызывается Logoff, если нужно - вот он и рвет все соединения (опять же, если класс не в состоянии ошибки).

Автор: Wound 20.07.20, 20:30
Цитата JoeUser @
Решил переписать "по-фэншую", и как оказалось, выплыла шляпа ;) Когда без RAII - критикуют мол, забудешь освободить ресурс. А как оказалось, в моей проге, пришлось в каждую лямбду вставить проверку на ошибку. Типа, если действительно была ошибка - освобождать, а если нет - пропустить.

Если бы переписал по феншую, скорее всего шляпы не было бы. Значит написал не по феншую :-? Если у тебя вызов сишной функции возвращает код возврата - кто тебе мешает кидать исключение в конструкторе RAII обертки? Тогда и проверять не пришлось бы в деструкторе.

Автор: D_KEY 20.07.20, 20:49
Цитата JoeUser @
Тогда нарушается логика поведения класса
...
Если логин не прошел - экземпляр класс болтается с состоянием ошибки

Плохая практика, не надо так делать. Прочитай про инварианты класса.

Автор: Qraizer 20.07.20, 21:41
Цитата JoeUser @
Типа, если действительно была ошибка - освобождать, а если нет - пропустить. И только в одной лямбде это не потребовалось.
Ну так это не RAII. В RAII нужно оборачивать ресурс, для которого известно время жизни, обозначив его {}, а не который когда захотел, получил, когда захотел, освободил. Если когда захотел, то это ж ничем не отличается от ручного управления. Посмотри в пример использования во втором "костыле", это пример из реальной программы... впрочем, мне не жалко скопипастить и даже прокомментировать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      // Атомарно получить ресурс монопольного доступа к образу приложения и захватить его
      HANDLE sameApp = CreateMutex(NULL, TRUE, uniqName.c_str());
     
      // Вручную проверить успех получения ресурса
      if (sameApp == NULL)
        return static_cast<void>(logMsg("CreateMutex() failed", GetLastError()));
     
      // Зарегистрировать освобождение ресурса
      Guard sameAppGuarded([=]() { CloseHandle(sameApp); });
     
      // Проверить, удалось ли захватить монополию или же доступ уже ранее (GetLastError() будет равен ERROR_ALREADY_EXISTS) был кем-то захвачен
      if (GetLastError() == ERROR_ALREADY_EXISTS)
        if (WaitForSingleObject(sameApp, INFINITE) != WAIT_OBJECT_0)        // подождать возможности монопольного доступа
          // При ошибке монополизации гвард сам освободит ресурс
          return static_cast<void>(logMsg("Recursive run detected for " + name.filename().string(), GetLastError()));
      // Монополия успешно получена, зарегистрировать его отдачу другим страждущим
      Guard sameAppLocked([=]() { ReleaseMutex(sameApp); });
     
      // Локальные границы времени жизни файлов, в которые перенаправляется консольный ввод/вывод
      {
        SECURITY_ATTRIBUTES attr = { sizeof(attr), NULL, TRUE };
        // Файл для перенаправленного консольного ввода
        HANDLE hIn  = CreateFile((newName.parent_path() / "stdin.txt").string().c_str(),
                                 FILE_GENERIC_READ, FILE_SHARE_DELETE,&attr, CREATE_ALWAYS,
                                 FILE_ATTRIBUTE_NORMAL, NULL);
        // Зарегистрировать его закрытие
        Guard hInGuarded ([=]() { CloseHandle(hIn);  });
    /* и так для всех трёх стандартных консольных устройств,
       ну и кроме того там ещё куча логики работы с образом приложения */
      }
      // и тут эти локальные границы заканчиваются, файлы с консольным вводов/выводом закрываются гвардами.
      // Ниже есть ещё чуток кода для логгирования и ещё по мелочи, и гварды позаботятся и об освобождении образа приложения от монополии, и об освобождении ресурса
В твоём случае тебе достаточно чётко определить времена жизни своих ресурсов и обозначить их {}, в частности вложенными функциями, если надо. И всё. Изнутри наружу хоть экспешном выходи.

Автор: JoeUser 20.07.20, 21:45
Цитата Wound @
Если у тебя вызов сишной функции возвращает код возврата - кто тебе мешает кидать исключение в конструкторе RAII обертки?

Не разобравшись - советуешь!
При чем тут вообще исключения? Вопрос в другом. В случае успешного прохождения полной цепочки инициализаций - ничего освобождать не нужно. После выхода из процедуры отдельные ресурсы должны оставаться валидными. Но, если не вся цепочка пройдена - вот тогда все ресурсы, захваченные до ошибки, должны быть освобождены.

Цитата D_KEY @
Плохая практика, не надо так делать. Прочитай про инварианты класса.

Цитата
Инвариа́нт — это свойство некоторого класса (множества) математических объектов, остающееся неизменным при преобразованиях определённого типа.

В данном случае никаких противоречий не вижу. Класс изначально проектировался для соблюдения состояний:

1) Ожидает
2) В состоянии соединения
3) В состоянии ошибки
4) В состоянии передачи

Причем возможные переходы между состояниями заранее предопределены. В этом случае инвариантность не нарушается.

Добавлено
Цитата Qraizer @
Ну так это не RAII

Получается - скоуп экзиты штоле тогда? :-?

Автор: JoeUser 21.07.20, 00:08
Цитата Qraizer @
Быстренько налобал правильный тест.

Скинь, плс, исходники потестить.

Автор: D_KEY 21.07.20, 00:11
Цитата JoeUser @
После выхода из процедуры отдельные ресурсы должны оставаться валидными. Но, если не вся цепочка пройдена - вот тогда все ресурсы, захваченные до ошибки, должны быть освобождены.

Это решается путём передачи владения ресурсом полю или передачи его(владения) наружу.

Добавлено
Цитата JoeUser @
В данном случае никаких противоречий не вижу. Класс изначально проектировался для соблюдения состояний:

1) Ожидает
2) В состоянии соединения
3) В состоянии ошибки
4) В состоянии передачи

Причем возможные переходы между состояниями заранее предопределены. В этом случае инвариантность не нарушается.

Допустим. ИМХО, тогда лучше воспользоваться паттерном state и управление ресурсами в этом случае будет примерно такое, как я описал. Но дело твое. В методе load в случае успеха передать владение от локального объекта полю все равно никто не мешает. Если не понятно, то могу код привести чуть позже.

Добавлено
https://godbolt.org/z/zbhGr5

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
    #include <memory>
     
    struct my_initializer {
        my_initializer()
        {
            std::cout << "init" << std::endl;
        }
        ~my_initializer()
        {
            std::cout << "deinit" << std::endl;
        }
    };
     
    struct my_struct {
        void load()
        {
            auto init = std::make_unique<my_initializer>();
            // ...
            bool success = true;
     
            // ...
     
            if (success) {
               init_.swap(init);
            }
        }
     
    private:
        std::unique_ptr<my_initializer> init_;
    };
     
    int main()
    {
        my_struct a;
        {
            std::cout << "enter" << std::endl;
            a.load();
            std::cout << "exit" << std::endl;
        }
    }


Поигравшись с переменной success сможешь понять, в чем суть.

Автор: JoeUser 21.07.20, 01:41
Не, это уже чересчур! Еще и локальные переменные заводить дополнительно, потом их обменивать ... ради чего? Попахивает антипаттерном.

Автор: D_KEY 21.07.20, 02:35
Это передача владения ресурсом.
Код простой, никаких дурацких флагов, if, goto, понятно, кто ресурсом владеет. Не надо даже деструктор у my_struct писать. Да даже guard и лямбд.
Просто сравни с твоей лапшой.

По поводу локальных объектов и обмена - гугли про безопасность исключений (на самом деле это касается любой обработки ошибок, если подумать). Если сам не найдешь толкового материала, я позже могу раскрыть тему.

Автор: JoeUser 21.07.20, 06:26
И у меня нет "дурацких флагов", есть описатели состояния. И goto уже нет. Говоришь нет дурацких if'ов:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    if (success) {
      init_.swap(init);
    });

По-моему уже пришли к вопросу бутерброда, с какой стороны правильно намазывать масло. В моем варианте - обеспечить неудаление нужного. В твоем варианте - обеспечить передачу владения. Что по сути звучит. и то, и то одинаково: не потерять лишнее. Но раз, ты нагородил еще более моего, обеспечивая, по сути, тоже самое - вот я и говорю. Антипаттерном попахивает.

Автор: Wound 21.07.20, 07:27
Цитата JoeUser @
Не разобравшись - советуешь!
При чем тут вообще исключения? Вопрос в другом. В случае успешного прохождения полной цепочки инициализаций - ничего освобождать не нужно. После выхода из процедуры отдельные ресурсы должны оставаться валидными. Но, если не вся цепочка пройдена - вот тогда все ресурсы, захваченные до ошибки, должны быть освобождены.

Почитай как работает система исключений в С++ при конструировании объекта.
На ка тебе пример еще в догонку, расскажи пожалуйста что тут работает не так, как ты описал? Очень интересно послушать.
https://ideone.com/SI8Kz6
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
    using namespace std;
     
    struct Resource
    {
        Resource(int id)
        {
            
            m_id = id;
            if(id == 3)
            {
                throw "Error during initialize: id can't be 3";
            }
            std::cout << "initialize resource with id: " << id << std::endl;
     
        }
        
        ~Resource()
        {
            std::cout << "Destroy Resource with id: " << m_id << std::endl;
        }
        
    private:
        int m_id;
    };
     
    struct ResourceWrapper
    {
        ResourceWrapper(int id1, int id2, int id3, int id4, int id5)
        : m_rc1(id1),
          m_rc2(id2),
          m_rc3(id3),
          m_rc4(id4),
          m_rc5(id5)
        {
        }
        
    private:
       Resource m_rc1;
       Resource m_rc2;
       Resource m_rc3;
       Resource m_rc4;
       Resource m_rc5;
    };
     
    int main()
    {
        
        {
            std::cout << "without errors:" << std::endl;
            ResourceWrapper rc(5,6,7,8,9);
        }
        
        std::cout << "==========================" << std::endl;
        try
        {
            std::cout << "with errors:" << std::endl;
            ResourceWrapper rc(1,2,3,4,5);
        }
        catch(const char* error)
        {
            std::cout << "Error: " << error << std::endl;
        }
        // your code goes here
        return 0;
    }

Автор: D_KEY 21.07.20, 07:32
Цитата JoeUser @
Говоришь нет дурацких if'ов

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

Цитата
В твоем варианте - обеспечить передачу владения.

В чем тут трудность или проблема-то? У ресурсов должны быть владельцы - это хороший подход, очень многие вещи упрощает.

Цитата
Но раз, ты нагородил еще более моего

Меньше :)
И код гораздо проще. И ошибиться в нем довольно сложно, гораздо сложнее, чем у тебя.

Но кто я такой, чтобы мешать тебе продолжать писать говнокод? :D

Добавлено
Цитата JoeUser @
В моем варианте - обеспечить неудаление нужного. В твоем варианте - обеспечить передачу владения. Что по сути звучит. и то, и то одинаково: не потерять лишнее.

Я не понимаю тебя, если честно.

Давай начнем с простого.
Вот это, на твой взгляд, одинаково "звучит":

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int * p = new 10;
     
    // ...
     
    delete p;


И

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto p = std::make_unique<int>(10);
     
    // ...


?

Автор: Wound 21.07.20, 07:46
Цитата D_KEY @
Это передача владения ресурсом.

Чем то похоже на идиому copy-and-swap https://ru.wikipedia.org/wiki/Copy-and-swap

Автор: D_KEY 21.07.20, 08:02
Цитата Wound @
Чем то похоже на идиому copy-and-swap

Ну да. Что бы ни произошло в методе load, это не затронет состояние, пока работа не будет завершена. Сначала делаем все операции, которые могут привести к ошибке, потом меняем состояние (тут уже ошибок быть не может).

Хотя конкретно тот мой код просто пример, а не реальный код.

Автор: JoeUser 21.07.20, 10:03
Цитата Wound @
Почитай как работает система исключений в С++ при конструировании объекта.

:blink: а то я не знаю?

Цитата Wound @
На ка тебе пример еще в догонку, расскажи пожалуйста что тут работает не так, как ты описал? Очень интересно послушать.

1) Ты собираешь независимые ресурсы в свой wrapper (хотя логичнее назвать holder). У меня они зависимые. Т.е. результат одного передается в инициализацию другого. Но и это не главное.
2) Один (или несколько, пока один) из ресурсов у меня "временный" и его хранить не нужно. Т.е. r1,rc2, rc3, rc4 проинициализировали, все rc3 уже не нужен, удалили, потом - rc5, rc6
3) Ну и самое наверное важное - ты решил в свой wrapper запилить в конструкторе больше чем нужно. Т.е., как объект создается - так он сразу бежит логиниться. Такое мне не нужно!

Но я понимаю, к чему ты клонишь - раздербанить мой sftp_class в целую "инфраструктуру", типа:

winsock_class - WSAStartup /WSACleanup();
libssh2_class - libssh2_init / libssh2_exit
resolv_slass - getaddrinfo / freeaddrinfo
socket_class - socket / closesocket
connect_class - connect / shutdown
libssh2_session_init_class - libssh2_session_init / libssh2_session_free
libssh2_sftp_sesstion_class - libssh2_sftp_init / libssh2_sftp_shutdown

Потом мне придется писать такую же портянку исключений, чтобы понять кто бросил, и можно ли продолжить работу ( допустим сделать еще 3 попытки соединения)

В результате вся эта "правильность" вылезает боком в увеличении кода и сложности взаимодействий. Пусть это четырежды правильнее - но самому находить работу, ради чистоты рассы, имхо - тупо и глупо! Лучше уж костыли, чем велосипеды с мотором от трактора.

Добавлено
ADD: Нет, ну я понимаю, когда система сложная, когда сложно отследить все взаимодействия - тогда нет вопросов. Но при таком линейном выполнении городить огород, просто, чтобы "соответствовало идеологии", не считаю это разумным.

Автор: Wound 21.07.20, 10:22
Цитата JoeUser @
:blink: а то я не знаю?

Судя по тому что ты мне ответил - возникает ощущение, что не знаешь. :-?

Цитата JoeUser @
1) Ты собираешь независимые ресурсы в свой wrapper (хотя логичнее назвать holder). У меня они зависимые. Т.е. результат одного передается в инициализацию другого. Но и это не главное.
2) Один (или несколько, пока один) из ресурсов у меня "временный" и его хранить не нужно. Т.е. r1,rc2, rc3, rc4 проинициализировали, все rc3 уже не нужен, удалили, потом - rc5, rc6

Совершенно не очевидно что у тебя там происходит, когда ты пишешь это словами. Но я не вижу пока каких либо проблем. :-?

Цитата JoeUser @
3) Ну и самое наверное важное - ты решил в свой wrapper запилить в конструкторе больше чем нужно. Т.е., как объект создается - так он сразу бежит логиниться. Такое мне не нужно!

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


Цитата JoeUser @
Но я понимаю, к чему ты клонишь - раздербанить мой sftp_class в целую "инфраструктуру", типа:

winsock_class - WSAStartup /WSACleanup();
libssh2_class - libssh2_init / libssh2_exit
resolv_slass - getaddrinfo / freeaddrinfo
socket_class - socket / closesocket
connect_class - connect / shutdown
libssh2_session_init_class - libssh2_session_init / libssh2_session_free
libssh2_sftp_sesstion_class - libssh2_sftp_init / libssh2_sftp_shutdown

Это не я к этому клоню - это принцип идиомы RAII. Один ресурс - одна обертка. В твоем случае у тебя тут выходит вот столько вот оберток, сколько ты тут написал. opn/close - один RAII класс/ну или смарт поинтер, в заивисимости от сложности реализации инициализации/уничтожения. Это вроде как стандартный подход :-?

Цитата JoeUser @
Потом мне придется писать такую же портянку исключений, чтобы понять кто бросил, и можно ли продолжить работу ( допустим сделать еще 3 попытки соединения)

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

Цитата JoeUser @
В результате вся эта "правильность" вылезает боком в увеличении кода и сложности взаимодействий. Пусть это четырежды правильнее - но самому находить работу, ради чистоты рассы, имхо - тупо и глупо! Лучше уж костыли, чем велосипеды с мотором от трактора.

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

Я конечно понимаю, что когда ты привык везде писать goto и юзать сырые указатели, понять ценность такого подхода сложно. Это все в основном приходит с практикой наверное. :-?

Добавлено
Но я бы тебе советовал перейти на другой ЯП, желательно с GC, тебе не нужен С++, ты просто не юзаешь его фишки как бы :-?

Добавлено
Да хоть тот же Си юзай, он поди быстрее С++ и меньше памяти рантайм жрать будет.

Автор: JoeUser 21.07.20, 10:42
Цитата Wound @
Судя по тому что ты мне ответил - возникает ощущение, что не знаешь.

Судя по тому, как ты судишь - ты не умеешь судить. 1-1 :) Ланна, Киля, спасибо за участие, я тебя услышал! :good:
D_KEY, и тебе спасибо :good:

Автор: Wound 21.07.20, 10:49
Вот посмотри как твоя задача(ну или подобная) решается с помощью RAII -> https://habr.com/ru/company/ispsystem/blog/430488/

Добавлено
В итоге весь твой код в функции размазывается на классы - это основная библиотека как бы выходит. А дальше ты уже с ними работаешь где тебе нужно.

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

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

Добавлено
Вот тебе еще пример RAII оберток для ssh -> https://github.com/rubdos/libssh2pp/blob/master/libssh2.hpp

Автор: JoeUser 21.07.20, 11:28
Цитата Wound @
Вот тебе еще пример RAII оберток для ssh


Видел это. тут, кстати, недообернули)))

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #ifdef WIN32
      WSADATA wsadata;
      WSAStartup(MAKEWORD(2,0), &wsadata); <----------------это один ресурс в классе Session, и нужно чекать возврат
    #endif
       // Initialize libssh2 on a thread safe manner, count the session instances.
       libssh2::__libshh2_session_count_mutex.lock();
    ...
    ...
    ...
      this->_sess = libssh2_session_init(); <------------- а это уже второй ресурс в классе Session

Автор: Wound 21.07.20, 11:36
Цитата JoeUser @
Видел это. тут, кстати, недообернули)))

Возможно, однако самую идею иллюстрирует.

Автор: OpenGL 21.07.20, 11:55
Цитата JoeUser @
При чем тут вообще исключения? Вопрос в другом. В случае успешного прохождения полной цепочки инициализаций - ничего освобождать не нужно. После выхода из процедуры отдельные ресурсы должны оставаться валидными. Но, если не вся цепочка пройдена - вот тогда все ресурсы, захваченные до ошибки, должны быть освобождены.

Чем моё решение не устроило?

Автор: JoeUser 21.07.20, 12:14
Цитата OpenGL @
Чем моё решение не устроило?

Ну я уже там выше Киле писал о дроблении класса sftp_class ... Ты реализовал вот так:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // ...
    // Сохранение локальных ресурсов в переменные класса
        session  = std::move(local_session);
        socket   = std::move(local_socket);
        ssh_init = std::move(local_ssh);
    // ...

Ну уж если решил дробить, будь добр выделять не более чем по одному ресурсу на владельца. А их вот сколько вырисовывается:

Цитата JoeUser @
1) winsock_class - WSAStartup /WSACleanup();
2) libssh2_class - libssh2_init / libssh2_exit
3) resolv_slass - getaddrinfo / freeaddrinfo <----------------- можно не хранить, т.к. резольвится один первый раз
4) socket_class - socket / closesocket
5) connect_class - connect / shutdown
6) libssh2_session_init_class - libssh2_session_init / libssh2_session_free
7) libssh2_sftp_sesstion_class - libssh2_sftp_init / libssh2_sftp_shutdown

Автор: Qraizer 21.07.20, 13:52
Цитата JoeUser @
Скинь, плс, исходники потестить.
Та на. Почти не отличается от оригинала :D
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <string>
    #include <vector>
    #include <chrono>
    #include <algorithm>
    #include <memory>
    #include <iterator>
    #include <filesystem>
     
    #include <fstream>
    #include <sstream>
     
    #include <cstdio>
    #include <cstdlib>
     
    // Windows stuff
    #include <io.h>
    // Linux stuff
    #include <sys/types.h>
    /*#include <sys/stat.h>*/
    #include <fcntl.h>
    /*#include <unistd.h>*/
     
    std::string load1(const std::string& path) {
        std::string buf;
        buf.reserve(std::filesystem::file_size(std::filesystem::path(path)));
        std::ifstream file(path);
        std::copy((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>(), std::back_inserter(buf));
        return buf;
    }
     
     
    std::string load2(const std::string& path) {
        auto ss = std::ostringstream(std::string(std::filesystem::file_size(std::filesystem::path(path)), '\1'));
        std::ifstream file(path);
        ss << file.rdbuf();
        return ss.str();
    }
     
     
    std::string load3(const std::string& path) {
     
        auto close_file = [](FILE* f){fclose(f);};
     
        auto holder = std::unique_ptr<FILE, decltype(close_file)>(fopen(path.c_str(), "rb"), close_file);
        if (!holder)
          return "";
     
        FILE* f = holder.get();
     
        if (fseek(f, 0, SEEK_END) < 0)
          return "";
     
        const long size = ftell(f);
        if (size < 0)
          return "";
     
        if (fseek(f, 0, SEEK_SET) < 0)
            return "";
     
        std::string res;
        res.resize(size, '\1');
     
        // C++17 defines .data() which returns a non-const pointer
        for (int i = 0; i < size; ++i)
          fread(&res[i], 1, 1, f);
     
        return res;
    }
     
    std::string load4(const std::string& path) {
     
        int fd = open(path.c_str(), _O_RDONLY);
        if (fd < 0)
            return "";
        
        struct stat sb;
        fstat(fd, &sb);
     
        std::string res;
        res.resize(sb.st_size, '\1');
     
        // C++17 defines .data() which returns a non-const pointer
        for (int i = 0; i < sb.st_size; ++i)
          read(fd, &res[i], 1);
        close(fd);
     
        return res;
    }
     
    // --------------------------------------------------------------------------------
     
    class Test {
        const std::string path;
        const int k = 10;
        uint64_t ref_time = 0;
     
    public:
        Test(const std::string& p) : path(p) {}
     
        void run() {
            measure("C++ istreambuf_iterator",  load1);
            measure("C++ stream::rdbuf",        load2);
            measure("libc fread",               load3);
            measure("POSIX read",               load4);
        }
     
    private:
        template <typename FUNCTION>
        void measure(const char* name, FUNCTION load_function) {
     
            auto load = [this, &load_function]() {return load_function(path);};
     
            printf("%-30s: ", name); fflush(stdout);
            uint64_t time = -1;
            for (int i=0; i < k; i++) {
              time = std::min(time, measure(load));
              putchar('.'); fflush(stdout);
            }
     
            printf(" %10llu us", time);
            if (ref_time == 0)
                ref_time = time;
            else {
                printf(" (%0.2f)", ref_time/double(time));
            }
     
            putchar('\n');
        }
     
     
        template <typename FUNCTION, typename time_type = std::chrono::microseconds>
        uint64_t measure(FUNCTION fun) {
            const auto ts = std::chrono::high_resolution_clock::now();
            const auto s  = fun();
            const auto te = std::chrono::high_resolution_clock::now();
     
            static volatile size_t size = s.size();
     
            return std::chrono::duration_cast<time_type>(te - ts).count();
        }
    };
     
     
    int main() {
        for (int i=1; i <= 32; i<<=1)
        {
            const std::string path = "test" + std::to_string(i) + ".fil";
            std::ofstream(path.c_str(), std::ios::binary).write(&*std::vector<char>(i*1024*1024).begin(), i*1024*1024);
            printf("File %s\n", path.c_str());
     
            Test test(path);
            test.run();
        }
     
        return 0;
    }

Автор: JoeUser 21.07.20, 15:38
Цитата Qraizer @
Та на. Почти не отличается от оригинала

С посимвольным чтением на libc и POSIX ты конечно отмочил! :lool:
Тест же должен же был выявить максимальные скорости чтения. А ты им такую подляну кинул)

Лучше бы два первых теста как-то забустил, типа:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    char Buffalo[64*1024*1024];
    ...
    ...
    std::string load1(const std::string& path) {
        std::string buf;
        buf.reserve(std::filesystem::file_size(std::filesystem::path(path)));
        std::ifstream file(path);
        file.rdbuf()->pubsetbuf(Buffalo, 64*1024*1024); // <-------------------
    ...
    ...
    ...

Автор: Qraizer 21.07.20, 16:55
Цитата JoeUser @
С посимвольным чтением на libc и POSIX ты конечно отмочил! :lool:
Ненуачё. Ему можно тролить, мне нельзя, что ль? :lol:

Добавлено
И это ещё как посмотреть, кто тролил, а кто нет. Формально только rdbuf() по определению читает всё скопом, хоть и всё рано не знает, сколько там. А вот istream_iterator совместно с back_insert уже на подобное не способны, и то, что ты видишь, является оптимизацией уровня библиотеки. Так что получите и распишитесь – libc и ОС API наглядно демонстрируют возможности оптимизации: руками не оптимизируешь, хрен и получишь.

Добавлено
Та и никто в здравом уме не будет выбирать конкретный интерфейс и свято ему следовать "шаги по сторонам == расстрел". Нормальные пацаны берут все необходимые интерфейсы и юзают от каждого по потребности. Нужны блочные бинарные операции – std::ifstream::read(), нужны символьные форматирующие – std::basic_istream<>::operator>>(), понадобились файловые – std::filesystem, итп. Так что у него там по-любому синтетика, на практике бесполезная.

Автор: OpenGL 21.07.20, 17:54
Цитата JoeUser @
Ну уж если решил дробить, будь добр выделять не более чем по одному ресурсу на владельца.

Я не знаю, как лучше сделать конкретно в твоей программе. Если это имеет смысл (например, если это не единственное место, где нужны эти ресурсы), то выноси. А так я сделал только по числу меток cleanup и чисто для демонстрации того, как это сделает здоровый плюсовик, а не плюсовик курильщика.

Автор: JoeUser 21.07.20, 22:09
Цитата OpenGL @
(например, если это не единственное место, где нужны эти ресурсы)

Вооо, вернулись к началу начал :lol: Сколько я уже страниц бъюсь, пытаюсь объяснить - для моего примера важно только конечное состояние при выделении этих всех ресурсов! А именно , добрались ли мы до логина в SFTP или нет. Если добрались, все эти ресурсы в полях класса висят без какого-то использования, только для удаления. И, по-сути, их все можно считать одним ресурсом. Ибо "частичный" логин (например, создали сокет, а соединиться не можем) смысла не имеет, и все откатываем в начало. Поэтому - гвардов вполне достаточно, хоть и таких мудреных с if-фами.

Автор: D_KEY 21.07.20, 22:13
Цитата JoeUser @
Если добрались, все эти ресурсы в полях класса висят без какого-то использования, только для удаления.

Так ведь именно так и будет в том случае, который тебе предлагают :) Я так и не понял что тебе не нравится.

А если речь об указателях (т.е. ресурсы в виде указателей у тебя), то тебе не нужно писать классы-обертки. Достаточно unique_ptr и typedef/using

Добавлено
Тут ведь что важно. Код всяких load станет проще и чище. Да, за счет дополнительных утилитарных структурок/typedef.

Добавлено
Цитата JoeUser @
Поэтому - гвардов вполне достаточно, хоть и таких мудреных с if-фами.

Если тебе нравится лапша в коде, то кто ж тебе помешает? :D

Добавлено
JoeUser, еще я понял, что есть разница в восприятии у нас. Ты как будто весь код целиком смотришь. Потому для тебя и нет разницы, где ресурс будет освобожден, в коде функции load или в отдельном объекте-владельце, даже второй случай кажется тебе более сложным. Я же предпочитаю декомпозировать. И мне важнее, что код load более простой и чистый, не замылен лапшой с очисткой ресурсов.

Автор: D_KEY 21.07.20, 22:31
Кстати, я иногда поражался способности некоторых людей быстро анализировать сложный запутанный код с большим количеством переходов и пр.
Это задача не для средних умов явно.
А я в таком коде точно сделаю кучу багов и буду не способен поддерживать его уже через пару дней после написания.

Так что для таких как я остается только декомпозировать, максимально упрощать, делать небольшие блоки, которые отвечают за что-то одно, максимально автоматизировать, писать юнит-тесты и т.д. и т.п.

Автор: JoeUser 21.07.20, 22:31
Цитата D_KEY @
А если речь об указателях (т.е. ресурсы в виде указателей у тебя), то тебе не нужно писать классы-обертки. Достаточно unique_ptr и typedef/using

Ну у меня почти поровну разных. Первые два - только "факт" что промежуточная инициализация прошла (чтобы потом финализировать). Третий - нужно сразу удалить память после и не хранить. А вот остальные три - указатели на различные структуры.

Цитата D_KEY @
Если тебе нравится лапша в коде, то кто ж тебе помешает?

Лапша в коде - это генерация "автономных" RAII там, где это неуместно. Во всех мануалх и бест-практиках твердят: используйте инструментарий там, где это уместно. Так вот тут, я считаю, лапша - это искусственное разбиение единого процесса, только с одной целью - впилить туда всеми правдами и неправдами RAII. И лапша и костыли - вот это и есть такое навязывание. Короче. Возвращаюсь к goto. Ваши попытки - неубедительны :lol:

Автор: D_KEY 21.07.20, 22:35
Цитата JoeUser @
Лапша в коде - это генерация "автономных" RAII там, где это неуместно.

Это не лапша, потому что это уже другой код, он к функции load не имеет отношения прямого. Можно отдельно писать (даже разными людьми) и затем поддерживать.
Вот тебе нужно будет поменять что-то в коде load. Тебе придется заново обдумывать, не сломалось ли чего. Мне не придется. У меня логика работы с ресурсами отделена.

Добавлено
Цитата JoeUser @
Во всех мануалх и бест-практиках твердят: используйте инструментарий там, где это уместно.

Да, RAII уместно использовать для работы с ресурсами.

Цитата
искусственное разбиение единого процесса

Это не единый процесс, ИМХО.

Цитата
Короче. Возвращаюсь к goto. Ваши попытки - неубедительны :lol:

Да кто ж тебе мешает-то? :)

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

Автор: JoeUser 21.07.20, 22:50
Цитата JoeUser @
Можешь потом код класса скинуть? Может на RAII переведу, чтоб была полная картина для спора.

Да не вопрос. Я может быть его и на гитхаб выкину (кстати, переписываю его без Qt), . Потому как норм библиотеки для SFTP нету. А платные я покупать не намерен! Вот там и форкнешь.

Цитата D_KEY @
Это не лапша

Нет, это лапша! :)

Цитата D_KEY @
Это не единый процесс

Нет, это единый процесс! :) Полу-логина не бывает.

Добавлено
Цитата D_KEY @
Вот тебе нужно будет поменять что-то в коде load

Для это изначально нужно тщательнее продумывать. И решать лучше проблемы по мере поступления. Раз пришлось потом что-то менять - значит это тебе урок: не ленись на начальном проектировании!

Автор: Wound 21.07.20, 23:20
Цитата JoeUser @
Если добрались, все эти ресурсы в полях класса висят без какого-то использования, только для удаления.

Что тебе мешает объявить эти ресурсы не в полях класса, а там где конкретно они нужны? Тогда они сразу же и удаляться, как только перестанут быть нужными.
В общем или лыжи не едут или я censored :-?
Тебе рассказывают про то, что хорошо бы работу с отдельными ресурсами вынести в отдельные классы, у тебя бы в таком случае код сократился бы в пять раз в твоей вырвиглазной функции, ты бы отделил логику работы приложения от логики работы с ресурсом, тем самым упростив понимание задачи.
Тут работы на максимум на пару часов, и того меньше, а ты третьи сутки сидишь - и то у тебя не так, и это у тебя не эдак.

Да тут даже вот взять если втупую не думая твой код скопипастить в класс, получается куда лучше и понятнее чем у тебя сейчас есть.
Допустим на примере сессии:
Какая то примитивная обертка(тупо взял все вызовы твоих функций и вынес в метод класса сессии):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct SLibssh2_session
    {
     
      SLibssh2_session()
      {
         m_session = libssh2_session_init();
         if (!m_session) {
           sftpError = sftp_error::session_error;
           throw "session is not initialized...";
         }
      }
     
      ~SLibssh2_session()
      {
        libssh2_session_disconnect(m_session, "Normal Shutdown, Thank you for playing!");
        libssh2_session_free(m_session);
      }
     
     
      void SetBanner(const std::string& banner)
      {
         if(!libssh2_session_banner_set(m_session, banner.c_str()))
         {
            sftpError = sftp_error::session_error;
            throw "can't set baner...";
         }
      }
      
      void Handshake(int socket)
      {
         if (libssh2_session_handshake(m_session, socket))
         {
            sftpError = sftp_error::handshake_error;
            throw "handshake_error...";
         }
      }
      
      void SetBlocking(int blocking)
      {
         // перевод в неблокируемый режим
         libssh2_session_set_blocking(m_session, blocking);
      }
     
    //! Эти методы возможно должны быть вынесены в другой класс, лень заморачиваться, просто ради показать идею.
     
      std::string HostKeyHash(int flag)
      {
         return libssh2_hostkey_hash(m_session, flag);
      }
      
      void LoginByUserPwd(const char* login, const char* pwd)
      {
          int ret = 0;
          while ((ret = libssh2_userauth_password(m_session, login, pwd)) == LIBSSH2_ERROR_EAGAIN);
          if (ret) {
            sftpError = sftp_error::auth_error;
            throw "error during LoginByUserPwd...";
          }
      }
     
      void LoginByPublickey_frommemory(...)
      {
            while ((ret = libssh2_userauth_publickey_frommemory(
                                            m_session,
                                            Auth->login.toLocal8Bit().data(),
                                            Auth->login.length(),
                                            Auth->public_array.data(),
                                            Auth->public_array.size(),
                                            Auth->private_array.data(),
                                            Auth->private_array.size(),
                                            Auth->password.toLocal8Bit().data()
                                        )) == LIBSSH2_ERROR_EAGAIN);
            if (ret) {
                sftpError = sftp_error::auth_error;
                throw "Error in LoginByPublickey_frommemory...";
            }  
        }
     
     
    private:
       LIBSSH2_SESSION* m_session;
    }

Использование, при этом логика твоего приложения никак не изменилась:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void mov::qt::sftp_class::slotLogin() {
        struct addrinfo hint;
        struct addrinfo *addrs;
        struct sockaddr_in *sin;
        const char *fingerprint;
        struct addrinfo *p;
        int ret;
        bool found;
        sftpError = sftp_error::no;
        if (sftpState == sftp_state::ready && Auth->authState == auth_state::ok) {
            // инициализация библиотеки
            if (libssh2_init(0) != 0) {
                sftpError = sftp_error::libssh2_error;
                return;
            }
            // поиск адреса хоста
            memset(&hint, 0, sizeof(hint));
            hint.ai_flags = AI_NUMERICHOST;
            hint.ai_family = AF_UNSPEC;
            hint.ai_socktype = SOCK_STREAM;
            hint.ai_protocol = IPPROTO_TCP;
            ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
            if (ret == EAI_NONAME) {
                hint.ai_flags = 0;
                ret = getaddrinfo(Host.toLocal8Bit().data(), NULL, &hint, &addrs);
            }
            if (ret != 0)               {
                sftpError = sftp_error::resolve_error;
                goto cleanup_libssh2_init;
            }
            found = false;
            for (p = addrs; p != nullptr; p = p->ai_next) {
                if (p->ai_family == AF_INET) {
                    found = true;
                    sin = reinterpret_cast<sockaddr_in *>(p->ai_addr);
                    break;
                }
            }
            if (!found) {
                sftpError = sftp_error::ip4_error;
                goto cleanup_libssh2_init;
            }
            // создание сокета
            sock = socket(AF_INET, SOCK_STREAM, 0);
            if (sock == INVALID_SOCKET) {
                sftpError = sftp_error::socket_error;
                goto cleanup_libssh2_init;
            }
            sin->sin_family = AF_INET;
            sin->sin_port = htons(Port);
            // коннект
            ret = ::connect(sock, (struct sockaddr *)(sin), sizeof(struct sockaddr_in));
            if (ret != 0) {
                sftpError = sftp_error::connect_error;
                goto cleanup_socket;
            }
    //======================Было=============================================
            // создание сессии
            // session = libssh2_session_init();
            // if (!session) {
                // sftpError = sftp_error::session_error;
                // goto cleanup_socket;
            // }
            // // установка баннера
            // if (libssh2_session_banner_set(session, "SSH-2.0-OpenSSH_LIBSSH2_1.9.0") != 0) {
                // sftpError = sftp_error::session_error;
                // goto cleanup_session;
            // }
            // // рукопожатие
            // if (libssh2_session_handshake(session, sock)) {
                // sftpError = sftp_error::handshake_error;
                // goto cleanup_session;
            // }
            // // перевод в неблокируемый режим
            // libssh2_session_set_blocking(session, 0);
     
            // получение отпечатка
            //fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
            // авторизация
            // if (Auth->authType == auth_type::login) {
                // while ((ret = libssh2_userauth_password(
                                                // session,
                                                // Auth->login.toLocal8Bit().data(),
                                                // Auth->password.toLocal8Bit().data()
                                            // ))   == LIBSSH2_ERROR_EAGAIN);
                // if (ret) {
                    // sftpError = sftp_error::auth_error;
                    // goto cleanup_session;
                // }
            // } else {
                // while ((ret = libssh2_userauth_publickey_frommemory(
                                                // session,
                                                // Auth->login.toLocal8Bit().data(),
                                                // Auth->login.length(),
                                                // Auth->public_array.data(),
                                                // Auth->public_array.size(),
                                                // Auth->private_array.data(),
                                                // Auth->private_array.size(),
                                                // Auth->password.toLocal8Bit().data()
                                            // )) == LIBSSH2_ERROR_EAGAIN);
                // if (ret) {
                    // sftpError = sftp_error::auth_error;
                    // goto cleanup_session;
                // }
            // }
     
    //=======================Стало============================================
            try
            {
                SLibssh2_session session;
                session.SetBanner("SSH-2.0-OpenSSH_LIBSSH2_1.9.0");
                session.Handshake(sock);
                session.SetBlocking(0);
                fingerprint = HostKeyHash(LIBSSH2_HOSTKEY_HASH_SHA1);
                if (Auth->authType == auth_type::login)
                {
                    session.LoginByUserPwd(Auth->login.toLocal8Bit().data(), Auth->password.toLocal8Bit().data());
                }
                else
                {
                    session.LoginByPublickey_frommemory(...);
                }
     
            }
            catch(const char* error)
            {
                error;
            }
    //====================================================================      
            // инициализация ftp-сессии
            do {
                sftp_session = libssh2_sftp_init(session);
                if (!sftp_session) {
                    if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
                        waitsocket(sock, session);
                    else {
                        sftpError = sftp_error::auth_error;
                        goto cleanup_session;
                    }
                }
            } while (!sftp_session);
            // все четко и дерзко
            sftpState = sftp_state::logged_in;
            return;
            // аварийная очистка
    //    cleanup_session:
    //        libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing!");
    //        libssh2_session_free(session);
        cleanup_socket:
           #ifdef WIN32
            closesocket(sock);
           #else
            close(sock);
           #endif
        cleanup_libssh2_init:
            libssh2_exit();
        }
    }


Добавлено
Разнеси оставшиеся ресурсы с очистками по классам, у тебя от функции останется 40 очевидных строк, которые будут понятны с полувзгляда.
Обертки сами - они так же примитивные, по сути отдельные операции суешь в методы, выделение ресурса - конструктор, удаление - деструктор.
Посмотри на получившийся класс - там сразу видно что это тупо обертка над Си функциями, ошибки детектятся мгновенно, т.к. каждая функция в отдельном методе, и если что можно сосредоточится на методе в 5 строк, нежели на функции в 200 строк вырвиглазного кода. Ошибку в 5 строках кода - найти проще, чем в 200 строках. Т.е. по сути класс ниочемный, но тем не менее - он логику работы с твоей сессией сократил в 3-4 раза.
А если еще прикрутить логирование, в каждый метод, то тут как бы вообще даже если и будет какой то баг, то его без отладки найти будет дело трех минут. В твоем коде не то что бы отладка, тут без двух литров не обойтись чтоб понять где там что произошло.

Автор: D_KEY 21.07.20, 23:48
Цитата JoeUser @
Раз пришлось потом что-то менять - значит это тебе урок: не ленись на начальном проектировании!

Нет :) Мир сложнее, чем ты думаешь. Если быть кратким и использовать модель Кеневин, то разработка софта почти всегда находится в запутанном домене, а не в сложном. То есть как бы ты ни собирал требования, как бы ни проектировал, все равно будут меняться требования и все равно нужно будет менять софт (если только мы политически его не законсервировали). Поэтому лучше как раз при проектировании направить усилия на возможность адаптации и поддержки. Ну в том случае, если речь не о hello world.
Собственно, большая часть паттернов проектирования на это направлено, как и большая часть современных инженерных практик в области софта (юнит-тесты, *DD, CI/CD и пр.), да и принципов тоже (KISS, DRY, противостояние over engineering).

Добавлено
Цитата JoeUser @
Да не вопрос. Я может быть его и на гитхаб выкину (кстати, переписываю его без Qt), . Потому как норм библиотеки для SFTP нету. А платные я покупать не намерен! Вот там и форкнешь.

:good:

А у тебя уже есть какие-то проекты выложенные?

Я только собираюсь начать в эту сторону идти, есть пара идей на поиграться.

Добавлено
Цитата JoeUser @
Нет, это единый процесс! :) Полу-логина не бывает.

Я про отделение логики от возни с ресурсами.

Добавлено
Цитата Wound @
Тебе рассказывают про то, что хорошо бы работу с отдельными ресурсами вынести в отдельные классы, у тебя бы в таком случае код сократился бы в пять раз в твоей вырвиглазной функции, ты бы отделил логику работы приложения от логики работы с ресурсом, тем самым упростив понимание задачи.

:good:

Автор: JoeUser 22.07.20, 00:04
Цитата D_KEY @
То есть как бы ты ни собирал требования, как бы ни проектировал, все равно будут меняться требования и все равно нужно будет менять софт

Ну надо же быть реалистом! В каждом конкретном случае. Да - неизменного нет. Начиная проектировать свою обертку, я задаюсь вопросом "какова вероятность, что процесс логина в SFTP сервер в ближайшее время изменится?" Исходные данные: "14 августа 2006 года было принято решение о прекращении работы над развитием протокола в связи с выполнением основной задачи проекта". Вывод: вероятность внесения новых изменений или требований - минимальная, если им только не взбредет в голову возобновить проект. Да, "никогда не говори никогда". Ну тогда мне, а вернее не мне, немножко не повезет. Но при такой практически нулевой вероятности - предусматривать последующий рефакторинг как минимум странно. Лучше уж к врачу паранойю лечить :)

Wound, Киля, астанавись!!! :lol: Я тебя услышал. Выложу код, форкнешь и покажешь мне свое минималистическое количество строк.

Автор: D_KEY 22.07.20, 00:11
Цитата JoeUser @
В каждом конкретном случае.

Согласен.

Цитата
Но при такой практически нулевой вероятности - предусматривать последующий рефакторинг как минимум странно. Лучше уж к врачу паранойю лечить :)

Так тебе никто не предлагает обмазаться всеми паттернами проектирования и пытаться предусмотреть изменения :)
Твой способ не быстрее и не проще способа с RAII, скорее наоборот.

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

Автор: JoeUser 22.07.20, 00:37
Цитата D_KEY @
Твой способ не быстрее и не проще способа с RAII, скорее наоборот.

Это всем апологетам RAII так хочется видеть. Потом начинаются конструкторы, деструкторы, смартпоинтеры ...а у меня всего лишь скромная метка для goto. Ты просто еще не проникся дзеном простоты, не понимаешь всего величия и фундаментальности безусловных переходов! :lol:

Добавлено
Цитата D_KEY @
Это как с принципом избегать преждевременной оптимизации. Принцип прекрасен, но он не отменяет того факта, что не стоит и "пессимизировать", т.е. если у тебя есть два одинаковых по сложности способа что-то сделать, то логично выбрать тот, что оптимальнее. Это не будет преждевременной оптимизацией.

И я с тобой согласен тут. Но это если действительно одно лучше другого. А если нет? Только Бритва Оккама и спасает.

Автор: Qraizer 22.07.20, 02:49
Цитата JoeUser @
Ты просто еще не проникся дзеном простоты, не понимаешь всего величия и фундаментальности безусловных переходов!
Посмею напомнить, что искусство программирования называется внезапно искусством. Код должен быть не просто правильным, он должен быть ещё и красивым.
Лично мне вот пофик, кто где как какие паттерны заюзал. Если я смотрю код, и он прекрасен, значит всё было сделано правильно.

Добавлено
:jokingly: А если он ещё и работает...

Автор: Gonarh 22.07.20, 06:48
Цитата Qraizer @
Если я смотрю код, и он прекрасен, значит всё было сделано правильно.

Субъективизм.

Добавлено
Цитата Qraizer @
Добавлено
:jokingly: А если он ещё и работает...

А вот это правильно :lol:

Автор: Wound 22.07.20, 07:14
Цитата JoeUser @
Это всем апологетам RAII так хочется видеть. Потом начинаются конструкторы, деструкторы, смартпоинтеры ...а у меня всего лишь скромная метка для goto. Ты просто еще не проникся дзеном простоты, не понимаешь всего величия и фундаментальности безусловных переходов!

Это все можно сравнить со стиркой. У тебя подход какой - ты постирал все вещи, что были прям кучей - носки/белое/черное/цветное/джинсы/футболки там всякие - вытащил всю эту кучу из машинки, и бросил на полку. Вот твой подход. Дальше придет время взять шмотку - будешь рыться искать.
Подход RAII - во первых стирать все по отдельности носки - отдельно, белое отдельно от черного, цветное отдельно, затем после стирки ты все вещи достаешь, выглаживаешь, и раскладываешь по полочкам, футболки к футболкам, брюки к брюкам, аккуратно сортируешь носки, засовываешь носок в носок(чтоб потом парные не искать), после чего раскладываешь на полочку.

Вот примерно этим отличается твой подход от подхода RAII. Да в случае с RAII - приходится сортировать и выглаживать, чтоб потом пришел - взял что тебе нужно, пошел. А с твоим подходом - оно то с виду может и быстрее взять кучу одежды с машинки и кидануть на полку, только потом ты всеравно потратишь то же самое время, а то и больше - на поиск вещи и на ее глажку. Не понимаю как можно не понимать столь очевидных вещей. :-?
Написание даже полноценного RAII класса выходит быстрее - чем выделение ресурса руками, потом проверка всех условий, потом переход по метке(это же еще понять нужно где ее правильно воткнуть надо будет, чтоб и лишней не было и мало не было), и освобождение его руками.

Автор: OpenGL 22.07.20, 14:48
Цитата D_KEY @
Кстати, я иногда поражался способности некоторых людей быстро анализировать сложный запутанный код с большим количеством переходов и пр.

Это да. Я, например, хочу мозг, способный удерживать в голове вот такой код :D (вот эта игра).

Добавлено
И кстати, JoeUser-у на заметку - даже в таком коде нет ни одного goto :D

Добавлено
Цитата JoeUser @
только с одной целью - впилить туда всеми правдами и неправдами RAII

Это не цель, а средство вообще-то.

Автор: Qraizer 22.07.20, 14:57
Цитата Gonarh @
Субъективизм
А то. Вот представь, что ты увидел в продакшне вот сие:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      std::wostream(&std::wbuffer_convert<std::codecvt_utf8_utf16<wchar_t>>(std::cout.rdbuf())) <<
            static_cast<std::wostringstream&&>(
                    std::wostringstream() << std::wostream(
                            &std::wbuffer_convert<std::codecvt_utf16<wchar_t, 0x10FFFF, std::little_endian>
                                                 >(std::ifstream("u16.txt").rdbuf())
                                                          ).rdbuf()
                                              ).str()
                                                                                                << std::endl;
:blink:

Добавлено
Ведь круто в одну строку выдать на консоль файл в UTF-16, кастанув на лету в UTF-8?

Автор: JoeUser 22.07.20, 16:26
Цитата OpenGL @
Это не цель, а средство вообще-то.

Для некоторых личностей - скорее цель ;)

Автор: D_KEY 22.07.20, 17:39
Цитата JoeUser @
Это всем апологетам RAII так хочется видеть. Потом начинаются конструкторы, деструкторы, смартпоинтеры ...а у меня всего лишь скромная метка для goto.

Так с меткой goto код становится сложнее. Хотя бы потому, что у меня в коде ее просто не существует.
С конструкторами, деструкторами и смартпоинтерами код становится проще :)

Цитата
Ты просто еще не проникся дзеном простоты, не понимаешь всего величия и фундаментальности безусловных переходов! :lol:

Я когда-то вполне себе писал на чистом си и использовал паттерн с goto для очистки ресурсов. И код такой видел часто. Но в си выбора нет.
С RAII же все в разы проще.

Добавлено
Более того, в питоне я использую with. И в других языках я так же предпочитаю использовать управление ресурсами, максимально приближенное к автоматическому.
RAII тут лучше всего. Ну разве что bracket pattern с ним может поспорить.

Добавлено
Цитата JoeUser @
Для некоторых личностей - скорее цель ;)

Тебе кажется. Тут конкретная задача - управление ресурсами. RAII как раз для этого.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)