Оформление вложенных блоков кодов.

Тема в разделе "WASM.HEAP", создана пользователем slesh, 8 сен 2011.

  1. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    slesh
    А по-моему, очень даже пристойно выглядит. И вложенность здесь абсолютно ни к чему. Это в lisp'е такого рода вещи удачно укладываются во многоступенчатую вложенность, но лисп вообще древовидный язык, и в нём движение вглубь дерева кода к детям, внукам и пра-пра-пра-...внукам текущего нода столь же просто и естественно как движение вдоль, перебирающее братьев-сестёр. Но за это лисп и не любят: завершения блоков кода вида ))))))) многих раздражает. В C же получается ещё хуже. Я как-то разглядывал программку, ещё досовскую, в которой было много-много вложенных блоков, именно с целью обработки ошибок, там десяток уровней вложенности был нормой. Это был просто абзац. Разобраться в коде я так и не смог. Но если вам кажется иначе... В такой ситуации я ничем не могу помочь.
    Вообще же, я разглядывал массу различных программ написанных на C с разными стилями оформления кода. И ядерный K&R стиль, по-моему, самый адекватный. Конечно же, вопросы выбора стиля очень субъективны, но вот на мой взгляд создатели языка С предложили самый адекватный стиль, всё последующие потуги что-то исправить либо сводятся к дополнениям к K&R (CodingStyle@Linux), либо просто ужасны и неудобны. Скажем, сорцы gcc вроде вполне читаемы, несмотря на гнутый стиль, но стоит только подольше в них поковыряться, как начинается отрыжка от функций длиной в двадцать экранов. Найти нужную строку в программе гораздо сложнее. Хотя, с другой стороны, такие функции нисколько не вредят удобочитаемости gtk+.
     
  2. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    2slesh

    Код (Text):
    1. result = 0;
    2. goto end1;
     
  3. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    +1 linux kernel coding style
    +1 goto -- это не зло, а необходимость в случае, если не используются исключения
     
  4. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Функции с большой вложенностью - предпочитаю принцип do{}while(0);.
    Только через макро-расширения, чтобы читалось нормально:
    Код (Text):
    1. #define BLOCK_BEGIN do{
    2. #define BLOCK_END   }while(0);
    3. #define BLOCK_EXEC(s)   if((s)==0)break;
    4.  
    5. ...
    6.  
    7. BLOCK_BEGIN
    8.     BLOCK_EXEC ((hFile = CreateFile (...)) != -1)
    9.     BLOCK_EXEC ((nBytes = GetFileSize (hFile, 0)) > 0)
    10.     BLOCK_EXEC ((p = VirtualAlloc (0, nBytes, ...)) != 0)
    11.     // ... etc.
    12. BLOCK_END
    13. //
    14. // Release resources here ...
    15. //
    Если мешает if(hfile)fclose(hfile);, то можно написать небольшие функции для этого.

    P.S. Если есть несколько аллокаций подряд, то лучше делать одним блоком:
    Код (Text):
    1. char* buf1 = malloc (SIZE1 + SIZE2 + SIZE3);
    2. char* buf2 = buf1 + SIZE1;
    3. char* buf3 = buf2 + SIZE2;
    4.  
    5. ...
    6.  
    7. free (buf1);
     
  5. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    izl3sa
    Всего лишь неаргументированное ИМХО? А ИМХО красиво.
    Нет там никакой кучи. Если для Вас основной работой при написании кода является собственно набор, а не продумывание его построения, то либо Вы очень крутой программист, либо очень хреновый.
    "Это бредятина". © Если вложенность задействована так, что одного взгляда на индентацию достаточно, чтобы понять ход работы кода (как в посте #14), это хорошо. Конечно, вложенностью часто злоупотребляют, как в следующем примере:
    Код (Text):
    1. VOID HighlyNestedFun(BOOL flag1, BOOL flag2, BOOL flag3)
    2. {
    3.     if (flag1)
    4.     {
    5.         if (flag2)
    6.         {
    7.             if (flag3)
    8.             {
    9.                 //process flag1 = TRUE, flag2 = TRUE, flag3 = TRUE
    10.             }
    11.             else
    12.             {
    13.                 //process flag1 = TRUE, flag2 = TRUE, flag3 = FALSE
    14.             }
    15.         }
    16.         else
    17.         {
    18.             if (flag3)
    19.             {
    20.                 //process flag1 = TRUE, flag2 = FALSE, flag3 = TRUE
    21.             }
    22.             else
    23.             {
    24.                 //process flag1 = TRUE, flag2 = FALSE, flag3 = FALSE
    25.             }
    26.         }
    27.     }
    28.     else
    29.     {
    30.         if (flag2)
    31.         {
    32.             if (flag3)
    33.             {
    34.                 //process flag1 = FALSE, flag2 = TRUE, flag3 = TRUE
    35.             }
    36.             else
    37.             {
    38.                 //process flag1 = FALSE, flag2 = TRUE, flag3 = FALSE
    39.             }
    40.         }
    41.         else
    42.         {
    43.             if (flag3)
    44.             {
    45.                 //process flag1 = FALSE, flag2 = FALSE, flag3 = TRUE
    46.             }
    47.             else
    48.             {
    49.                 //process flag1 = FALSE, flag2 = FALSE, flag3 = FALSE
    50.             }
    51.         }
    52.     }
    53. }
    В таких случаях от вложенности надо избавляться. Например, таким паттерном:
    Код (Text):
    1. VOID UnrolledHighlyNestedFun(BOOL flag1, BOOL flag2, BOOL flag3)
    2. {
    3.     enum {y = -1, n = 0, flag1_set = 1, flag2_set = 2, flag3_set = 4};
    4.    
    5.     DWORD state = (flag1 ? flag1_set : 0) | (flag2 ? flag2_set : 0) | (flag3 ? flag3_set : 0);
    6.    
    7.     switch (state)
    8.     {
    9.     case ((flag1_set & y) | (flag2_set & y) | (flag3_set & y)):
    10.    
    11.     break;
    12.     case ((flag1_set & y) | (flag2_set & y) | (flag3_set & n)):
    13.    
    14.     break;
    15.     case ((flag1_set & y) | (flag2_set & n) | (flag3_set & y)):
    16.    
    17.     break;
    18.     case ((flag1_set & y) | (flag2_set & n) | (flag3_set & n)):
    19.    
    20.     break;
    21.     case ((flag1_set & n) | (flag2_set & y) | (flag3_set & y)):
    22.    
    23.     break;
    24.     case ((flag1_set & n) | (flag2_set & y) | (flag3_set & n)):
    25.    
    26.     break;
    27.     case ((flag1_set & n) | (flag2_set & n) | (flag3_set & y)):
    28.    
    29.     break;
    30.     case ((flag1_set & n) | (flag2_set & n) | (flag3_set & n)):
    31.    
    32.     break;
    33.     }
    34. }
    Преимущество такого паттерна ещё и в том, что одинаковую обработку для каких-то сочетаний очень легко организовать (в отличие от примера с избыточной вложенностью).
    r90
    То же самое можно сказать про использование goto. Есть разница между необходимостью с целью реализации алгоритма и необходимостью с целью повышения читабельности кода.
     
  6. slesh

    slesh New Member

    Публикаций:
    0
    Регистрация:
    6 фев 2009
    Сообщения:
    214
    2 l_inc, ну незнаю, то что ты продемонстрировал только усложняет понимание кода.
    Первый вариант очень хорошо читаемый и логика очень понятная. А вот второй вариант довольно сложный для прочтения, а также в первом варианте до любой операции можно добраться за 3 условия, то в твоем потребуется намного больше проверок, что может сказать на скорости выполнения кода (пускай и не сильно изменится, но как гласит народная мудрость - копейка рубль бережет).
     
  7. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    slesh
    Когда Вы смотрите на следующий фрагмент кода (не весь ведь код помещается на экран, особенно с учётом полезной нагрузки), Вы можете быстро определить, при каких условиях он выполняется?
    Код (Text):
    1.    else
    2.             {
    3.                 //вот например, этот блок
    4.             }
    5.         }
    6.     }
    7.     else
    8.     {
    9.         if (flag2)
    Думаю, что нет, т.к. нужно пролистать наверх к началу вложенных условий и проанализировать, в каком именно месте находится рассматриваемый блок кода.
    Здесь же:
    Код (Text):
    1.     case ((flag1_set & y) | (flag2_set & n) | (flag3_set & n)):
    2.    
    3.     break;
    достаточно просто взглянуть на заголовок блока, т.к. этот код является хорошим комментарием к самому себе. Это насчёт читаемости. Теперь по поводу быстродействия.
    В следующей строке (как и во всей функции) вообще никаких проверок не будет.
    Код (Text):
    1. DWORD state = (flag1 ? flag1_set : 0) | (flag2 ? flag2_set : 0) | (flag3 ? flag3_set : 0);
    Любой компилятор догадается сделать этот код без ветвлений комбинацией neg+sbb+and.
    Единственный безусловный переход во всей функции будет в точке switch (state), где адрес назначения прыжка будет выбираться из таблицы переходов, сформированной компилятором.

    P.S. Ах да. Ещё безусловные переходы по break. Аналогичные ставятся в любом if-then-else перед началом else-блока.
     
  8. qwe8013

    qwe8013 New Member

    Публикаций:
    0
    Регистрация:
    28 май 2009
    Сообщения:
    198
    l_inc
    Для этого комментарии есть.

    А вот от этого:
    Код (Text):
    1. ((flag1_set & y) | (flag2_set & n) | (flag3_set & n)):
    в глазах рябит.
     
  9. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    qwe8013
    От чего? От двух подряд идущих скобок? Мне не рябит. Я показал, как я делаю и как считаю правильным делать. Кому нравится, возьмёт на заметку. Если Вам не нравится, делайте по-своему.
     
  10. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    l_inc
    Не совсем понял мысль вашу. Вообще весь CodingStyle -- это повышение читабельности кода и ничего больше. Сочинители linux'ового CodingStyle сочли, что всё что угодно можно написать следуя правилам перечисленным в CodingStyle. А если у кого-то не получается, значит он просто не умеет писать на C. Вот и всё.
    Если ко всем ограничениям из CodingStyle добавить ещё и запрет на goto, то я боюсь, что в мире не найдётся специалиста, который сможет писать программы следуя этому CodingStyle: жёсткие ограничения на количество идентированных блоков и требование иметь ровно один return в функции, при отсутствии goto вынудят дробить код на много очень мелких функций. А это приведёт к тому, что задачи выполняемые каждой функцией будут уже столь мелки и незначительны, что... Короче, по-моему, получится не очень хорошо.
     
  11. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    r90
    Я так понял, Вы разделяете идею linux CodingStyle. По крайней мере мысль, которую я процитировал:
    Например, мне часто надо больше уровней. Но это не означает, что я бы то же самое на трёх уровнях не реализовал. Реализовал бы, конечно, но код бы стал менее понятным. Из чего и следует моё замечание о том, что принимать это ограничение в качестве неукоснительного правила — не самая удачная идея.

    Насчёт goto я не имел в виду, что запрет на него — это дополнительное ограничение. Имелось в виду, что с тем же успехом можно взять какой-нибудь someproject coding style, где, например, нет ограничения на вложенность, но запрещено использовать goto (тем более, что такой вариант гораздо более распространён). При этом аналогично можно сформулировать принцип: "А если программисту всё-таки надо goto, значит хреновый он программист", — согласно которому ядро линукс написано хреновыми программистами.
     
  12. PSR1257II

    PSR1257II New Member

    Публикаций:
    0
    Регистрация:
    25 июн 2011
    Сообщения:
    228
    Мне близки принципы упомянутые by r90 - goto, последовательное "развертывание" функционала и последовательный "откат" и так далее. Несколько замечаний.

    0) Код может быть как самой главной процедурой модуля/проекта так и какой-нить низкоуровневой функцией которую часто вызывают но которая меняется очень редко (DebugMsg, OutString, etc). Понятно, что требования к разным участкам кода должны быть также разные - например в центральной функции какого-нить банковского модуля где сосредоточена наиболее верхняя и важная логика - что самое важное? Быстродействие? На уровне логики - безусловно! (как наиболее эффективно строить сложные запросы к БД например). Также важна "масштабируемость" - в условиях часто меняющихся бизнес-требований это намного важнее чем какая-то там "вложенность".

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

    1) Много говорят о том как оформлять процедуры - но это вовсе не самое главное! Каркас программы - который закладывает архитектор в самом начале - вот кто определяет придется ли мне чесать голову чтобы найти все возможные места на которое повлияет мое изменение или нет;

    2) Много говорят о том как "надо" писать код но мало говорят о критериях - почему именно так а не иначе? Даже рекомендации от самых крутых прогеров звучат неубедительно для многих если они просто директивы без объяснения (или им слишком тупо следуют личности без критического образа мышления).

    Что же может быть критерием? Как можно убедить прогера (например) использовать "последовательную развертку" функционала:

    Код (Text):
    1.   if ( !(hFile1= CreateFile(...)) ) goto Exit1;
    2.   if ( !(hFile2= CreateFile(...)) ) goto Exit2;
    3.   ...
    Попытки указать ему на то, что код растет направо натолкнется на "резонное" замечание "а я щас поставлю новое супер IDE с отличной подсветкой и все будет ништяк" - и что вы скажете в ответ? Сравнивать размеры болтов?

    Таким "независимым" (sic!) критерием может быть, например, величина "сжатия" компилером вашего C-кода. Если при разборе ассемблера который нагенерил хороший компилер видно что он спотыкается о ваш код (он не понимает паттернов и получается хуже чем было бы если бы человек писал на асме) - может говорить что вы пишете неправильно (или слишком круто для компилера) - однако вы в любом случае должны понимать почему так было сконпелировано...
     
  13. weiden

    weiden pupkin

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    17
    Опу ТС и всем негодующим могу посоветовать Макконела "Совершенный код" (the code complete) - хорошо расписано о стилях и вкусах в написании кода.
     
  14. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.622
    Адрес:
    Russia
    стиль l_inc имеет свои преимущества и иногда решение будет красивее и изящнее
    пример такого случая задачка для студентов
    решение через вложенные условия - ад
     
  15. 2a59a821970f4f7a

    2a59a821970f4f7a New Member

    Публикаций:
    0
    Регистрация:
    18 авг 2011
    Сообщения:
    3
    2slesh
    а что мешает подключить
    #include <boost/scope_exit.hpp>
    или подключить #include <loki/scopeguard.h>
    очень удобно они компиляторм разворачиваются в
    чистый машинный код без зависимостей и берут всю тяжкую рабюоту по слежке на себя
    например надо выпонить какое то действие прни выходе пишем
    Код (Text):
    1. BOOST_SCOPE_EXIT((&hEvent))
    2. {
    3.     // ... тут пишем код который выполнится при выходе из блока
    4.     // ... странная запись & означает передачу аргуметов по ссылке
    5. }BOOST_SCOPE_EXIT_END;
    или вот возмем LOKI_ON_BLOCK_EXIT
    выделили ресупс и сразу пишем так
    Код (Text):
    1. LOKI_ON_BLOCK_EXIT(CloseHandle, hEvent);
    2. или если надо по ссылке передать то
    3. LOKI_ON_BLOCK_EXIT(CloseHandle, Loki::ByRef::(hEvent));
    выполнится так же при выходе из блока ну и по возможности
    смотреть на готовые с++ обертки типа ATL чтобы не парится
    с освобождением ресурсов в крайнем случае я использую
    если врапперов нет или BOOST_SCOPE_EXIT или LOKI_ON_BLOCK_EXIT
     
  16. 7d407e345f84708

    7d407e345f84708 New Member

    Публикаций:
    0
    Регистрация:
    15 сен 2011
    Сообщения:
    2
    2slesh
    а что касается оформления то я за вложенные блоки если их немного
    если много то я за подход с гото вызов проверка прыжок на освобождение
    или продолжаем дальше но это к большим функциям только относится
    которые логически делают одно дело а дробить ради того что бы
    соблюсти кодинг стиль маразм

    ставьте мне +5 я самые дельные советы дал
     
  17. 7d407e345f84708

    7d407e345f84708 New Member

    Публикаций:
    0
    Регистрация:
    15 сен 2011
    Сообщения:
    2
    2slesh
    как пишут код си хакеры ? если блоков немного они делаю их
    вложенными если много юзают гото как отличить новичка от профи ?
    новичок разделит единое целое на 5+ функций что бы небыло вложенности
    при этом задачу размажет так что ее вкурить будет трудней
    хакер использует гото и разместит одну задачу в одной функции
     
  18. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    Rockphorr
    Не. Нисколько. Смотри:
    Код (Text):
    1. int main()
    2. {
    3.     int ns[3];
    4.     int imin = 0, imax = 0;
    5.     int i;
    6.     scanf("%d%d%d", ns, ns+1, ns+2);
    7.     printf("%d, %d, %d\n", ns[0], ns[1], ns[2]);
    8.     for(i = 1; i < 3; i ++) {
    9.         if(ns[imin] > ns[i])
    10.             imin = i;
    11.         if(ns[imax] < ns[i])
    12.             imax = i;
    13.     }
    14.     ns[imin] += ns[imax];
    15.     ns[imax] = ns[imin] - ns[imax];
    16.     ns[imin] -= ns[imax];
    17.     printf("%d, %d, %d\n", ns[0], ns[1], ns[2]);
    18.     return 0;
    19. }
    Если хочешь, я могу ещё и для десяти переменных написать, и нисколько при этом не запутаться во вложенных условиях. ;)

    7d407e345f84708
    Поубивал бы таких хакеров вместе с их кодом. Разобраться в их мешанине невозможно. Они не C-хакеры, они asm-хакеры, которые влезли в мир C как заурядные нубы, позабыв посмотреть на отличия C от asm.
     
  19. Rockphorr

    Rockphorr Well-Known Member

    Публикаций:
    0
    Регистрация:
    9 июн 2004
    Сообщения:
    2.622
    Адрес:
    Russia
    r90
    не, там без циклов и массивов это какбы дальше по программе

    а щас

    Код (Text):
    1. input(a,b,c)
    2.  print(a,b,c)
    3.  ... ; <-- здесь вся кухня по сравнению abc между собой и обменом значениями через d
    4.  print(a,b,c)
    и если использовать стиль то хватит трех "ифов"
     
  20. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    Rockphorr
    Что значит без циклов и массивов? У меня они и так, "как бы дальше по программе", в начале стоит "int main()". Циклы и массивы неотъемлимая часть языка, и несколько странно утверждать, что какой-то там паттерн удачен лишь потому, что он позволяет обойтись без циклов и массивов.
    ps. Я не пытаюсь доказать неудачность паттерна, просто указываю на то, что приведённое "доказательство" его удачности несостоятельно.