вторник, октября 06, 2009

Google C++ Style Guide

Google C++ Style Guide как-то упомянули у меня в комментариях и я решила продублировать ссылку на него здесь. Это стандарт кодирования гугловых open source С++ проектов. Этот Style guide содержит некоторые спорные решения как то: отказ от исключений, отказ от RTTI.

В свое время Google C++ Style Guide активно обсуждался на Habrahabr'е.

42 коммент.:

Вячеслав комментирует...

А у нас на работе всё та же венгерская нотация... как я её недолюбливаю...

Андрей Валяев комментирует...

А я вот думаю, что Кодинг Стайл Гайд - это типа соглашение между программистами о том как нужно писать.

Только чем больше программистов учавствуют в этом соглашении - тем больше размер гайда.

Мыслю, что было бы правильно в каждой команде иметь свой минимал гайд... Соблюдать все правила из толстого-претолстого документа крайне сложно... кодинг стайл должен влезать на одну страницу, чтобы каждый мог повесить его на стенку в рамочке. :D

У нас тоже пока никакого нету, хотя венгерскую нотацию мы не используем, слава богу.

Алёна комментирует...

2Андрей Валяев:
Мыслю, что было бы правильно в каждой команде иметь свой минимал гайд...

Дык так и есть. Почти везде где я работала были внутренние стандарты кодирования. У вас не так?

Андрей Валяев комментирует...

У нас есть негласные правила... Мы еще только учимся.. :) Код ревьюв вот начали делать... Может скоро и документик какой сформулируем...

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

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

Алёна комментирует...

2Андрей Валяев:

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


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

Денис Радченко комментирует...

Венгерскяа нотация - какой-то отстой. Она была хороша, когда нужно было смотреть на код и представлять тип переменной. Зачем это в эпоху IDE?

Андрей Валяев комментирует...

2Денис Радченко: Ну изначально префиксы обозначали вовсе не это.

Джоэл про это писал...

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

Типа того: xPos = yPos - сразу видно что ерунда... :)

Но потом кто-то придумал кодировать тип, и пошло - поехало.

Константин Казимиров комментирует...

2 Андрей Валяев:
"глобальную категорию переменной" - не совсем точно... Скорее "суть, назначение", чтобы нельзя было

currentCell = currentRow;
даже если обе переменные типа int.
А префиксы определяющие область видимости были в оригинальном документе.

2Денис Радченко:
А не все сейчас IDE и используют... Мне, например, простую идею проще быстро набросать в блокноте, а потом вставить в Студию, ибо перегружена оная кучей фич.
И такой вопрос: коде ревью Вы тоже из IDE делаете?

Константин комментирует...

Вполне себе разумный coding standard. Особенно соглашусь с фразой "BE CONSISTENT".

Из спорных моментов отмечу неиспользование исключений, но и здесь мотивация ясна. К тому же: "Things would probably be different if we had to do it all over again from scratch." С остальным примириться совсем легко.

Андрей Валяев комментирует...

А меня еще раздражают отступы пробелами... по моему, на порядок проще один раз нажать на таб, чем выравнивать код пробелами (хотя для IDE и продвинутых редакторов это и не актуально)... У них какой-то микс K&R и GNU стайла...

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

Лично я кодревьюв делаю в kdiff3, который вызывается непосредственно из перфорса... ревьювить постоянно все - бессмысленно, надо смотреть на то, что изменилось.

Анонимный комментирует...

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

Alex Che комментирует...

Спасибо за ссылку, добавлю в свой ToRead-список.
В свое время для меня было в какой-то мере откровением прочесть "Joint Strike Fighter Air Vehicle, C++ Coding Standards, December 2005". Стандарт кодирования на С++, принятый в Lockheed Martin Corporation при разработке многоцелевого ударного истребителя. Не менее серьезная контора и серьезный проект.
Так вот там тоже попадались странные на первый взгляд правила, вроде отказа от исключений и рекурсий.
Кстати, по поводу исключений Спольски, если я не ошибаюсь, написал в той же самой статье, в которой он защищал Венгерскую нотацию (в оригинальной ее трактовке).

Анонимный комментирует...

Отказ от исключений предлагаю продолжить: предлагаю правило: отказ от множественных return в функции (гыгы).

Андрей Валяев комментирует...

Можно все запретить...

У меня все будут счастливы, а кто не будет - того я... (с) Бармалей

Только каждую возможность придумывали не просто так... у auto_ptr тоже есть свой смысл. На мой взгляд исключения сильно упрощают код. Конечно этот механизм тяжелый, и это надо понимать. И конечно необдуманным испрользованием чего угодно можно здорово испохабить код.. Дык это можно сделать и несмотря на все запреты. :)

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

Хотя для студентов - иначе никак. :) Это опытный программист понимает что к чему, а студент может написать любую ерунду.

ЗЫ: На испытательный срок - один кодинг гайд, 5 лет отработал - другой. Через 10 лет программирования тебя отводят в секретную комнату, где лежит совершенно секретный кодинг гайд, в котором написано что нет никаких гайдов... :D

Alex Che комментирует...

На то он и C++, чтобы был выбор и широкие возможности. За что я его и люблю. И именно из-за своих больших возможностей C++, как никакой другой язык, пожалуй, нуждается в подобного рода сборниках правил - они как база, от которой следует по умолчанию отталкиваться при проектировании или написании программ.
И, как и всегда, из всяких правил есть исключения (pun intended). Но чтобы их допускать, нужны веские причины и достаточные полномочия.

Flint комментирует...

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

Ну а насчет того, какая нотация лучше, так это тема для отдельно вынесенного холовара где нибудь на просторах rsdn

Анонимный комментирует...

Андрей Валяев: да-да :-) а кто не shared ptr, тот в Stl огребает море overhead'ов. И потом вопросы задает не "как сделать, чтобы не копировать сложные объекты в список", а "что вы возьмете, vector или list"? :-) А кто не auto ptr и не handle wrapper- зачем им С++ вообще?

Анонимный комментирует...

Написание ПО для истребителей с использованием исключений, RTTI... Вы лулзность этой фразы не понимаете?)

>> Но скорость имеет значение далеко не всегда.
if (!привет мир) throw упс!
да)

з.ы. я не троль :)

Андрей Валяев комментирует...

99,9% ПО разрабатывается не для истребителей!

Да и Google вроде не занимается такой ерундой, как истребители.

И чтобы вот так вот, из за истребителей, запрещать RTTI и исключения?

:D

Marat комментирует...

Не хочу разводить холивар, но:
1) как и написано у Страуструпа - исключения должны оставаться исключениями, т.е. "бросать" их надо в исключительных ситуациях (т.е. в тех ситуациях, которые не являются обычными, предусмотренными логикой программы ветвлениями)
2) без RTTI (сам лично не использую вообще) и исключений это уже не C++, так как даже new "бросает" исключение (и это правильно, что бы там не писали в Google и Mozilla)
3) насчет JSF Coding Standard - там все под embedded заточено и в этом плане все разумно написано, но есть пример того как "подружить" "return error code" и "exception" - см. как используется boost::system::error_code в boost::asio (этот пример хорош еще и тем, что там как раз не "return" используется для возврата "error code")
4) боязнь использовать что-то, потому что не на всех платформах компиляторы позволяют "это" (правильно) собрать (и в быстрый код) вполне разумна, но способствует увеличению парка недо-С++ компиляторов и сроку их жизни
5) надо трезво понимать, что серьезный код (больших объемов) так или иначе не сделать кроссплатформенным "от и до" (спросите у разработчиков Java-игр для мобильных телефонов) - в той или иной мере все равно придется проводить портирование - вопрос только в его объеме
6) исходя из [5] - не понятно, почему Google так боится исключений - единственное что приходит в голову - они просто не желают тратить время на обсуждение того, где можно "кидать exception", а где нельзя
7) ну и как вывод - без исключений не обойтись (если, конечно, не вывернуть и не "переизобрести" C++) и в любом случае их будут поддерживать все платформы (Windows CE и все, что на ней есть уже поддерживает) - вопрос только в том, всегда ли мы вправе "бросать" исключение - тут просто еще раз проштудировать Бьярна - у него пожалуй, самый взвешенный подход ко всему, что есть в C++

P.S. а мне все таки нравится JSF Coding Standard, но naming conventions больше импонируют STL и Boost C++ Libraries, хотя в если в команде принято другое (хорошо если еще JSF-подобное, а не Camel) то профессионализм :) трубет строго подчиняться.

Алёна комментирует...

2Marat:
6) исходя из [5] - не понятно, почему Google так боится исключений - единственное что приходит в голову - они просто не желают тратить время на обсуждение того, где можно "кидать exception", а где нельзя

За Google не скажу, но везде, где я работала, исключения были либо запрещены стандартами кодирования, либо не рекомендовались к использованию. Причины: исключения повышают сложность кода и ухудшают читаемость.
AFAIK, исключения не используются в российском геймдеве в принципе. Не знаю как там в забугорье дела обстоят.

adx комментирует...

В этом стандарте меня лчно напрягло то, что запрещается использовать директивы using. В сочетании с boost'овскими (да и просто std) библиотеками, где любят на каждый чих создавать namespace, код быстро замусорится уточнениями контекста.

boost::gil::opencv::resize(boost::gil::const_view(my_src), boost::gil::view(my_dst) );

(Пример навскидку)

Marat комментирует...

2Алёна:
1) Я не "бью в грудь" - юзайте исключения, но пытаюсь донести, что надо обязательно учитывать возможность появления исключений (хотя бы потому, что пишем на языке, в который они буквальныо "зашиты") - Java-way в плане исключений безобразен
2) предпочитаю
а) не бросать исключение, если можно обойтись без него,
б) гарантии Абрахамса, точнее стараюсь придерживаться только гарантии "неутечки ресурсов"
в) стараюсь не обрабатывать исключения (т.к. это по большей части ошибка) - просто даю программе "упасть" - тут я полностью согласен со Страуструпом
3) с практической стороны вызывает интерес то, как например, лично Вы поступаете с "new" (вот только не надо переписывать мнение разработчиков Qt или Mozilla - уже много раз читал, поэтому хватит просто ссылки)
4) Если использовать STL (или просто C++ Standard Library), то опять же без исключений "не получится" - понимаю, что в gamedev-е на С++ почти никто не использует STL (и C++ Standard Library? для чего же тогда они нужны? :)
5) использовать RAII и везде "защищаться" от исключений действительно не просто (посмотреть хотя бы в исходники boost::asio) - но исключения не так уж и запутывают код - сравните со множеством if-else (ACE vs. boost::asio).
6) "исключения для исключений" - если можно, то я стараюсь использовать boost::system::error_code (как в boost::asio - передавая его через ссылку), но есть и места, где без них не обойтись - конструктор, operator [] и т.д. любой знает эти примеры
7) насколько я вижу (и где-то читал - Вы наверняка тоже) - не такая уж и малая часть кода является no-throw.
9) все порываюсь изучить Intel TBB - неужели и там нет исключений? (я имею ввиду "совсем нет") - а это точно используется в "забугорном геймдеве" - недавно что-то там анонсировали в UE3
9) ...все таки холивар... так что простите... просто интересно мнение - все таки "вариться в собственном соку" еще никому не пошло на пользу.

P.S. Я далеко не профи в C++, хотя и хотел бы. Да и в production на C++ почти ничего не писал, так что очень ценю мнение опытных разработчиков и стараюсь не навязывать свое.

Алёна комментирует...

2Marat:
1) Я не "бью в грудь" - юзайте исключения

Да я в общем тоже без всякого подтекста говорю. Просто вот так дела обстоят.

3) с практической стороны вызывает интерес то, как например, лично Вы поступаете с "new"

У нас свой менеджер памяти, оператор new переопределен. При каких-либо проблемах с памятью выдаем assert, который есть только в дебаге и который вырезается в релизе.

9) все порываюсь изучить Intel TBB - неужели и там нет исключений? (я имею ввиду "совсем нет") - а это точно используется в "забугорном геймдеве" - недавно что-то там анонсировали в UE3

Про Intel TBB я совсем не в курсе.

9) ...все таки холивар... так что простите...

Да не, по-моему всё довольно конструктивно.

Marat комментирует...

2Алёна
С менеджером памяти - ожидаемо, т.е. многие так и делают (Mozilla). А чем тогда этот вариант отличается от "обычного new" - в release нет исключений? значит, все таки, возвращаемое значение всегда проверяется на NULL (пока все же правильнее говорить "0")? Если нет, то, значит, просто принимается, что если при тестах не было "out of memory", то и у пользователя тоже (никогда) не будет? Согласен, с тем, что это допустимо. Но "new" не единственное место - см. operator= и т.д. Конечно, операторы перегружать - последнее дело, но иногда все-таки нужно (как раз читабельность кода повышается). Я еще раз повторюсь, что прежде чем "бросить exception" надо "100 (10000...00) раз подумать" - аналогично и для try-catch. Вот в связи с этим, мне кажется, Google просто "боится" терять время на "священные войны". Хотя игнорирование RAII еще никому на пользу не пошло. Да и "хороший C++ программист" не должен злоупотреблять (именно злоупотреблять) всем тем, что есть в C++ (неужели Google "своих" такими не считает?). Кстати, как Вам "Herb Sutter, Andrei Alexandrescu - C++ Coding Standards"? Это конечно не Google (и уж тем более не JSF), но лично мне кажется хорошей отправной точкой для построения своего (т.е. для полного включения).

Алёна комментирует...

2Marat:
С менеджером памяти - ожидаемо, т.е. многие так и делают (Mozilla). А чем тогда этот вариант отличается от "обычного new" - в release нет исключений?

Ммм... Ну можно сказать и так, да. В release нет исключений. Это, конечно, не является основным смыслом существования менеджера памяти.

Marat комментирует...

2Алёна
Раз уж отошел от темы, то продолжу...

Что касается менеджера памяти:
1) интересно Ваше мнение по поводу http://www.hoard.org/
2) -//-//- по поводу memory allocator в Intel TBB (task memory allocator)
3) -//-//- custom memory allocation в boost::asio - конечно там из-за этого сильно осложняется (именно осложняется - как-нибудь соберусь и напишу про ее "тупую" реализацию) раздельная компиляция, но в "effective net code" это уже должно отходить на второй план.

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

P.S. Это последний "пост" - больше "сорить" не к чему.

Анонимный комментирует...

>> Причины: исключения повышают сложность кода и ухудшают читаемость.
я не верю всяким байкам о трудно читаемости при использовании goto, throw (и что там у вас ещё). скорость решает всйо. вротмненоги если в cryengine expressive c++! ибо в gamedev главное растрата тиков. не так ли? :))

Анонимный комментирует...

>> И чтобы вот так вот, из за истребителей, запрещать RTTI и исключения?
нет конечно :) более того я скажу что использую исключения (при инициализации ядра конструктор кидает если продолжать невозможно). но повсеместно использовать это глупо. давайте ещё virtual'ить все :))

Алёна комментирует...

2Анонимный:

я не верю всяким байкам о трудно читаемости при использовании goto, throw (и что там у вас ещё).

Не надо верить, правильно. Я проверяла goto на своей шкуре. Не понравилось.

в gamedev главное растрата тиков. не так ли? :))

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

Анонимный комментирует...

Очень быстрая и очень глюкавая игра никому не нужна
.net? :)

з.ы. вот почему cryengine и unreal делаются годами
з.з.ы. всё молчу :))

Анонимный комментирует...

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

Три года ранее. Компания Quest. Сервисы. Во всех случаях кидается исключение class Exc { strText, hresultCode } или его наследники- как писать по-другому тесты- не знаю. Сейчас использую в своей практике. Любой try заканчивается catch (Exc).

Собственно, точно так же, как в java писали try .. catch (throwable). Потому что проблемы натива нам пофик и были нужны исключительно для лога. И обязательно везде finally... иначе ресурсы ни фига не освободятся.

Думаю, WinAPI, тот же CreateFile внутри себя может легко кидать Exceptions. Наружу он дает код ошибки. По этому коду ошибки exception кидает уже мой Wrapper.

Эту же систему можно сделать в игре- один компонент внутри себя кидает унифицированное исключение Exc, сам его ловит на выходе и переделывает в код ошибки. Wrapper, использующий этот компонент, преобразует код ошибки в унифицированное исключение Exc... ну и т.д. :-) Смысла в скорости при передаче кода ошибки я не вижу :-).

Порадовали китайцы. В огромной системе из сотни проектов был только один, в котором разрешили exception'ы. Я специально переписывался с этим инженером, чтобы узнать, почему у него было просветление- ну, что он там использовал- медитацию, цигун... Оказалось, что он их потом запретил. "Потому что код уменьшается на N килобайтов".

Китайцы очень жестко следовали Qt: НИ ОДНОГО исключения. Только в коде не было анализа НИ ОДНОГО кода возврата. Для них НУЖНО писать throw "Wrong filename RTFM" :-).

P.S.: Автоматические тесты рулят.

Дмитрий комментирует...

Как по мне, то самое верное и важное утверждение по поводу стандартов кодирования: "Не столь важно, какие именно у вас стандарты кодирования, важно, что они есть, и все им следуют". Google C++ Style Guide - более чем достойный кандидат для основы стандартов кодирования и для размышлений в принципе. Особенно понравилось то, что для большинства решений есть описание, за, против и вывод.

Евгений Охотников комментирует...

Чесно говоря, очень странно читать после стольких лет существования исключений в мейнстримовых языках программирования про использование и неиспользование исключений. Тем более, что в ряде языков (Ruby, Python, Java, C#) -- исключения вообще являются единственной формой извещения об ошибках.

Уж не знаю, насколько сильно исключения в современных C++ных компиляторах сказываются на скорости работы. Но вот на надежность и отказоустойчивость программ исключения влияют гораздо лучше, чем коды возврата или глобальный errno.

dtjurev комментирует...

2Алёна:
>AFAIK, исключения не используются
>в российском геймдеве в принципе.

Почему же, есть компании, где используются вовсю. :)

>При каких-либо проблемах с памятью
>выдаем assert, который есть только
>в дебаге и который вырезается в
>релизе.

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

Алёна комментирует...

2dtjurev:
Почему же, есть компании, где используются вовсю. :)

:-)

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

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

Плюс у нас суровый defensive программинг. Значения проверяются, но перед обращением.

Анонимный комментирует...

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

Евгений Охотников комментирует...

Считаю, что эта тема для флудинга. не более того.

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

atz-2085 комментирует...

Где-то прочитал отличный вывод на тему всех таких попыток "оптимизировать" codestyle C++ (запрещая использование чего-то - как исключений в случае Google):

"Чтобы эффективно писать на C++, придется использовать ВСЕ возможности языка."

Это значит, что проектируя язык, Страуструп постепенно обнаруживал проблемы и закрывал их, выдумывая новые возможности, (эксцепшены, темплейты) поэтому отсутствие чего-либо - это невозможность сделать тот или иной код прямым. Т.е. отстрелянная нога СРАЗУ.

atz-2085 комментирует...

> Китайцы очень жестко следовали Qt: НИ ОДНОГО исключения. Только в коде не было анализа НИ ОДНОГО кода возврата.

Если очень жестко следовать Qt, то получится чудовищный код, в котором на каждый чих (i.е. в начале каждой функции, даже если она вида A+=B; ) делается до десяти проверок: а если у нас не нулевой указатель на параметры; а выглядит ли размер данных таким, как нужно; а доступны ли нам некие глобальные объекты (типа общего менеджера объектов - или он по каким-то причинам умер), а знает ли этот менеджер (который может быть удаленным сетевым сервером!) про объект, у которого мы собираемся что-то спросить...

Т.о. код раздувается в среднем в пять раз.

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

hydrevt комментирует...

Благодарю за интересную статью и комментировавших - за дискуссию. Только смените пожалуйста ссылку в посте - сейчас обсуждение на Хабре находится на http://habrahabr.ru/links/38329/
Причём прямой поиск результатов не даёт ( http://habrahabr.ru/search/?q=Google+C%2B%2B+Style+Guide ), пришлось походить "путями нормальных героев".

Alena комментирует...

hydrevt

Благодарю за интересную статью и комментировавших - за дискуссию. Только смените пожалуйста ссылку в посте

поправила, спасибо