Pull to refresh

Comments 46

Охх, спасибо огромное за такие посты. Появится время, обязательно посмотрю и покопаюсь в коде Coreutils. Очень часто начинающих программистов отправляют типа: иди присоединяйся к opensource проекту и качай себя. Вот Вы привели неплохой так сказать порог вхождения для Coreutils.
coreutils-4.5.10/lib/closeout.c — а сюда не заглядывали? :)
/* Close standard output, exiting with status STATUS on failure.
   If a program writes *anything* to stdout, that program should `fflush'
   stdout and make sure that it succeeds before exiting.  Otherwise,
   suppose that you go to the extreme of checking the return status
   of every function that does an explicit write to stdout.  The last
   printf can succeed in writing to the internal stream buffer, and yet
   the fclose(stdout) could still fail (due e.g., to a disk full error)
   when it tries to write out that buffered data.  Thus, you would be
   left with an incomplete output file and the offending program would
   exit successfully.

   FIXME: note the fflush suggested above is implicit in the fclose
   we actually do below.  Consider doing only the fflush and/or using
   setvbuf to inhibit buffering.

   Besides, it's wasteful to check the return value from every call
   that writes to stdout -- just let the internal stream state record
   the failure.  That's what the ferror test is checking below.

   It's important to detect such failures and exit nonzero because many
   tools (most notably `make' and other build-management systems) depend
   on being able to detect failure in other tools via their exit status.  */
Спасибо за подсказку, посмотрел! Только теперь оно переехало в gnulib/lib/closeout.c.
По-моему, код GNU — не лучшее место для того, чтобы смотреть, как следует писать профессиональные программы. Как-то раз пришлось покопаться в gettext — там просто мрак. Глобальные переменные, файлы длиной в несколько тысяч строк, сплошное дублирование кода. Да и стиль выравнивания с фигурными скобками, вынесенными на два пробела, превращает код в абсолютно нечитаемую кашу. Не могу даже представить, кому пришло в голову использовать такой стиль.
А как нормально?
Так?
int main() {

}
И с табами по 4 пробела? или по 8?
Думаю, имелось в виду
int main()
{
	...
}

либо
int main() {
	...
}
Второй вариант вообще не наглядно совершенно помом.
стандарт для Джавы
И K&R, и Allman оба хорошо читаются, и с табами по 4, и с табами по 8 (в последнем случае только если вы не делаете слишком много уровней вложенности).
Просто ради интереса приведу краткую цитатку из Linux Kernel Coding Style. Вот что Линус Торвальдс пишет про стандарт кодировани GNU:
First off, I'd suggest printing out a copy of the GNU coding standards,
and NOT read it. Burn them, it's a great symbolic gesture.
И нужно сказать: Хорошо написал
Да, форматирование кода действительно спорное. Если не Coreutils, то какой проект для обучения вы бы посоветовали?
По поводу оформления GNU есть www.gnu.org/s/hello/ — hello world 2.7 размером в 2.5МБ с локализацией, страницами info и man + дополнительной документацией на TeX, правильными README, ChangeLog-ами, лицензиями, разными флагами, сборкой по automake/autoconf, да и ещё с юнит-тестами.

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

Поэтому присоединяюсь к вопросу.
Если есть проект, которому Вы хотели бы в будущем помочь, лучше выбрать его — не зря потратите время на изучение кода. Если просто хочется что-нибудь почитать, можно попробовать посмотреть на glib. Это хорошо спроектированная и документированная библиотека, на которой держится весь Gtk/Gnome и много еще что. В ней много всего интересного — от структур данных до работы с сокетами. Там тоже GNU-стиль отступов, но файл перед чтением всегда можно пропустить через indent с нужными опциями :).

Можно посмотреть на что-нибудь из известных сетевых демонов — apache или proftpd, например. Или на ядро Linux, если есть интерес (про его внутренности даже книжки есть).

Если хочется увидеть настоящий «хакерский» код на C с множеством макросов и сокращений, можно посоветовать интерпретатор языка Lua (http://www.lua.org/source/5.1/). С одной стороны — разбираться сложно, но с другой — все очень компактно и продумано до мелочей.
Если интересно разобраться в multithreading, применении теории графов на практике и просто прекрасной билд системе то tup github.com/gittup/tup

SQLite — тоже интересный проект

Следующий уровень — чтение и понимание ядра Linux/FreeBSD. Не так уж все сложно там, просто сильно отличается от user-space программирования.
Посмотрите гугловские опенсоурсные продукты. Там всегда запредельные требования к качеству кода.
Я бы не сказал так. Сыроватость (и, местами, глюковатость) кода у гугла присутствует. Вот, мой любимый пример:

/**
* Always returns false.
*
* @return false
*/
public boolean isEnabled(int position) {
return true;
}


Источник
Сильно зависит от постановки задачи: Скажем так — скажите чему вы хотите научиться и я отвечу вам где и у кого этому надо учиться.

Даже стилистика кода иногда очень сильно диктуется постановкой задачи. Если например, вы хотите узнать как пишут highload вещи — смотрите исходники highload вещей. Если хотите посмотреть как пишут вещи заточенные на безопасность — смотрите исходники непробиваемых вещей. Хотите узнать как пишутся вещи, работающие с железом в kernel mode — смотрите исходники ядра и драйверов. и т.д.
Мне нравится Qt coding style, с учетом того, что он скорее для С++, но само форматирование там вполне удачное, на мой взгляд. Хотя мы заменили пробелы на табы у себя.
Мне нравится стиль написания nginx. Попробуйте глянуть его.
Кстати часто встречаю такую конструкцию для копирования буфера известной длинны
while(length--) *dst++ = *src++;


Что это? Копипаст? Почему не использовать что то типа mmcpy? Или надежда на умный компилятор, который все равно заменит на одну команду для процессора?
Лично я узнал об этой конструкции из книги Кёрнигана и Ритчи. Зачем это использовать где-либо кроме учебных программ не знаю :)
Заглянул в glibc. Если я правильно понял, то memcpy именно так и работает. По крайней мере в glibc.
Кроме такого базового, там ещё много различных вариантов, оптимизированных под различные наборы векторных команд.
>>Что это? Копипаст? Почему не использовать что то типа mmcpy?
Не думаю. Просто 1) Подобный код пишется на автомате в результате привитой привычки 2) Даже в такой мелкой функции можно «накосячить» это произойдет если не проверить наложение областей источника и получателя. А делать такую проверку не всегда целесообразно.

Да, по одной из такой функции mem* семейства Линус Торвальдс «гладил по головке» разработчиков одного из Open-source проекта. Насколько помню дело было в том что разработчики допустили косяк при разработке функции и произошло наложение областей src и dst, а с течением времени другие разработчики кто использовал функцию в своем коде просто принимали этот косяк как данность и в конечном итоге спустя время, в наши дни решили этот косяк убрать что привело к тому что одна из программ у Торвальдса перестала работать. На что он собственно корректно заявил «Не следует из править баг исправление которого нарушает работоспособность солидное множество других программ».
> дело было в том что разработчики допустили косяк при разработке функции и произошло наложение областей src и dst
Это был не «косяк», а реализация функции. В мануалах белым по-черному написано, что нельзя рассчитывать на то, что функция memcpy правильно отработает при перекрывании dst и src.

А некоторые несознательные граждане маны читать по-человечески не научились, вот и стали писать кривой код.
>>В мануалах белым по-черному написано, что нельзя рассчитывать на то, что функция memcpy правильно отработает при перекрывании dst и src.
Вот и я об этом же! Когда программист пишет:
>>while(length--) *dst++ = *src++;
Он хочет быть полностью уверенным в том что будет работать так как он это видит на экране, а не ждать возможных хитросплетений аналогичной функции из mem*-семейства.

У вас какая-то странная интерпретация этой истории.

Там всё проще, memcpy вообще ничего не гарантирует, но испокон веков сложилось, что она копирует вперед. Криворукие прогеры допустили баг, клоторый по счастливой случайности не был заметен из-за особенности реализации memcpy. Когда memcpy поменяли, то баг стал вылезать на некоторых процессорах. Это не косяк gcc, это косяк флеша, тут всё очевидно.
Ну так я и не стал утверждать на все 100% ;) Поэтому предварил словами «Насколько помню дело было...», а подразумевает, что человек может неправильно помнить ;)
Очень рад, что вы это привели в более точном варианте!
То, что это косяк флеша — это очевидно. Но вопрос тут в другом, стоит ли в Линуксе настолько держаться за обратную совместимость, чтобы ради неё сохранять некоторые баги в реализации и обьявлять их фичами.
В виндах с этим понятно, но там народ и систему покупал и хочет, чтобы старая прожка с новой осью работала. В Линуксах этой проблемы нету. Стоит ли оставлять костыли для кривого старья?
Он уже успел намозолить мне глаза в других программах и необходимость его наличия была для меня загадкой.
Это связано с тем, что любой файл проекта, выпущенного под GPL, можно взять и использовать в другом проекте отдельно. Его задача в том, чтобы авторство и правила использования при этом не потерялись.
GNU echo не такой уж и тривиальный. Чего стоит echo *!
Раскрытием «здёздочки» занимается шелл, который вместо нее подставляет список файлов. А чтобы к echo в argv попала именно "*" — нужно написать «echo \*» (которую этот echo и выведет). Я к тому, что это не заслуга echo :)
Это, наверно, нелёгкий опыт программирования по DOS/win даёт о себе знать.
GNU Utils были написано в уже достаточно далекие времена, и сейчас уже просто никто не будет переписывать то что так давно написано и самое главное — работает. Но прогресс уже ушел вперед.

А форматирование кода — это помоему совершенно не та вещь на которую нужно смотреть в проекте. Все современные IDE поддерживают выбор и настройку форматирования. Плюс еще и к тому же, если я не ошибаюсь, в самом gnu есть утилитка indent, которая позволяет изменять форматирование исходников.
UFO just landed and posted this here
Я как то хотел что то выдрать из GNU core utils (вроде md5sum) — зарекся. Настолько нечитаемый код…
Тогда я решил что я еще не дорос, что бы оценивать творчество бородатых дяденек, типа RMS.
Теперь я больше склонен думать, что читаемый код — это признак ясности мыслей. С этим похоже не все в порядке у авторов GNU.
> Переходим к исходному коду. Достать его можно либо с помощью apt-get source и получить версию, которая используется в вашей системе по-умолчанию, либо вытянуть новейшую версию из репозиториев.

Ещё можно достать с помощью emerge --fetch-only
Ни когда, ни когда, ни когда не используйте open source проекты как учебные.
Они иногда хуже поделок школьников. Или слишком запутанные, часто неоправданно (например, где можно было бы обойтись фиксированным массивом длиной в 256 элементов по 4 байте (число задается при старте программы и не меняется динамически), они городят огород из списка, который при 256 элементах отожрет на порядок больше. Зато при 1-2 используемых элементах действительно меньше памяти используется.

И еще много нареканий. Очень редко код является образцом кодинга. И ни когда я не видел проект с хорошими комментариями, маленькими функциями и логичностью (исключая случая, если это результат коммерческой разработки)
Я видел несколько проектов, готовящихся к открытию сорцов. Это такой спагетти-быдлокод, который можно собрать только на машине главного разработчика. Неудивительно что сорцы открываются так медленно, их просто показывать стыдно. Маленькие логичные функции? А что и зачем это? Работает, костыли при необходимости поставить можно и ладно.

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

Не спорю что контрпримеры можно найти и там и там, но раз уж вы рассказываете о своём опыте, расскажу и я о своём.
Из своего опыта хочу сказать, что открытость кода ни на что не влияет. Хотя открытый код обычно куда более кроссплатформенный и его проще собирать. Но качество кода зависит целиком от лидеров проекта.
Спасибо за статью! В мемориз.
Нужно обладать некоторым мужеством, чтобы подробно разбираться в коде opensource. Как-то мне нужна была реализация MPI_Gather, смотрела для сравнения в библиотеках mpich и openMPI. Была в шоке, насколько изящен и читабелен код первой и настолько мозгодробителен — второй.
Ну код проектов, которым больше 10 лет, вообще обычно то ещё зрелище. А если за время разработки ещё и 5 раз команда разрабов сменится, то вообще туши свет.
Опять же на вскидку можно сравнить код clang и gcc.
Sign up to leave a comment.

Articles