Fuck the standards!

TR1 уже давно вышел. Это хорошо. Студия 2008 и 2010 уже включают его по умолчанию. Это просто отлично.

Стандарт (а TR1 – это де-факто уже стандарт) – это всегда хорошо. Больше нельзя называться C++ программистом и закатывать истерики при виде безобидного bind. Теперь нужно либо напрячь мозги и таки освоить основы функциональщины, либо идти заниматься гуевой мышевозней.

А еще стандарты – это плохо. Потому что любое, даже самое разумное и оправданное изменение стандарта превращается в процесс перемещения и горы и Магомета к единому стандартизированному месту встречи. И стандартно занимает до чертиков времени, да и других восполнимых и не очень ресурсов тоже дофига требует.

Вот включили они bind в стандарт. И чо (что, шо, So what, нужное подчеркнуть)? А перегруженные операторы не включили! Бо не было их еще в бусте на момент подачи черновиков в стандарт. В итоге в TR1 вот так вот не сделать:

vector<Person> myVector;
//
// Some stuff here
//
vector<Person>::iterator iter = find_if(
myVector.begin(),
myVector.end(),
bind(&Person::GetLastName, _1) == "Pupkin");

А все из-за того, что тот bind, что в TR1, не перегружает оператор “==”, сцуко такой! В итоге, чтобы сделать то же самое, что на куске кода вверху, нужно городить двухуровневый bind с equal_to посередине, от вида которого даже у привычных к boost пассажиров может случится истерика:

vector<Person>::iterator iter = find_if(
myVector.begin(),
myVector.end(),
bind(equal_to<string>, "Pupkin", bind(&Person::GetLastName, _1)));

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

Нинавижу.

А следующего стандарта еще 10 лет ждать.

Workaround day

В 2010 студии M$ столько всего напеределывали, что создание новго проекта превратилась в поиск воркэраунда к воркэраунду. Например, зачем-то напрочь переделали определение custom build tool, причем гуй для его создания и редактирования привинтить забыли. В результате сегодня нашел, что сами  M$ рекомендуют проекты, которые используют custom build tool (ну, мало ли, вам Yacc’нуть чего надо или еще чего страшного натравить на исходники) создавать сначала в 2008 студии, а потом конвертировать в 2010.

Воистину, стоя и в гамаке. Еще и в противогазе.

К чести Студии, конвертирует она проекты без запинки.

Продолжаем есть кактус.

Рабоче-некрестьянское

А однажды мне пришлось реализовывать стек протокола.

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

Просто в этот раз протокол оказался что надо! Индустриальный (тут должно быть нецензурное междометие)!

Индустриальный протокол семейства ISO там или даже, не к ночи будет помянут, IEC – это Вам не нищебродский RFC793 какой-нибудь. Индустриальный протокол разрабатывали люди от индустрии. Специально обученные и нанятые. За ох большие деньги.  Стало быть, протокол должен быть продуман до последнего сообщения, в нем должны быть предусмотрены абсолютно все вещи, которые только могут понадобится в данной области промышленности, а каждый бит описан и запротоколирован, благодаря чему реализовывать оный должно быть проще, чем два байта об асфальт – знай себе следуй стандарту и дело в шляпе.

По крайней мере в теории. Как мы знаем, в теории разницы между теорией и практикой нет. На практике же…

На практике же сначала оказывается, что индустриальный (а стало быть, практически обязательный к исполнению, если хочешь, чтобы пацаны уважали) стандарт сначала нужно купить. За большие деньги, но дело даже не в этом, а в том, что процедура покупки набора PDF зачастую превращается в квест почище второго Ларри. Затем, по получению документов, может оказаться, что разобраться в ЭТОМ без применения веществ смогут только очень альтернативно одаренные пассажиры. Например, стандарт может оказаться представлен полутора десятком слабо связанных друг с другом PDF, орагнизованными таким образом, что каждый абзац каждого из них будет ссылаться на различные абзацы из всех других PDF (и еще одного отсутствующего) как минимум один раз, причем понять смысл абзаца без прочтения всего материала, на который они ссылаются, будет решительно невозможно. Сами же PDF  будут минимум наполовину состоять из “словарей терминов” и аббривеатур, которые сами по себе ничего не значат, но тем не менее постоянно используются повсюду, в результате чего к прыжкам по ссылкам добавятся постоянные попытки расшифровать, что де эта двенадцатибуквенная аббривеатура означает. Потом, даже если Вам посчастливится разобраться в хитросплетении перекрестных вовсе не гиперссылок, приправленных аббривеатурами и сокращениями, нигде более во Вселенной не встречающимися, понимания, откуда что берется, куда что уходит, зачем нужен вот тот параметер не прибавтися. Зато желание сменить профессию вот прямо сейчас заметно усилится. Соусом послужит непременный набор совершенно противоречивых данных о той или иной фиче, причем противоречия зачастую можно найти в одном и том же предложении.

К этому моменту любой наугад взятый RFC Вам уже покажется если не поэзией Пушкина, то как минимум хорошим захватывающим детективом. Конан Дойлем, например.

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

Вот так и работаем. А что делать?

Студия 2010: дневник мыши, поедающей кактус

На вкус кактус пока ничего. За прошедшие три дня студия упала три раза и два раза зависла.

Что заметно:

  • Переработали меню проекта “добавить новую хрень”.  Выглядит ново, почти вебдванольно. Эргономика, правда, не изменилась.
  • Кажется, переписали Intelli(non)Sense. Как и обещали. Работает заметно быстрее и менее косячно. Впрочем, проект у нас пока в коротких штанишках, посмотрим, что будет дальше
  • IntelliSense точно если не переписали, то сурово ковыряли. По крайней мере, в предыдущих версиях она могла ставить Студию в разные позы в случае использования C++/Cli. Типичный пример – при использовании MSTest для тестирования C++ кода Студия начинала уходить в нирвану при сколько-нибудь приличном количестве тестов в проекте. В новой версии IntelliSense просто заявляет, что для C++/Cli оно работать не будет. И, что характерно, не работает.
  • MSTest теперь по умолчанию хранит результаты лишь для 25 запусков. Я, в принципе, вообще не понимаю всей этой затеи с хранениями результатов юнит-тестов. С моей точки зрения, тесты либо прошли, либо нет. Если тесты не прошли, билд не проходит тоже. End of story. В любом случае теперь, кажется, студия перестанет захламлять диск историей тестов, которая все равно никому не нужна.
  • Хост-процесс для тестов ведет себя странно. Периодически забывает завершиться, ввиду чего пересборка DLL с тестом обламывается. Подобного заскока за прежними версиями замечено не было.
  • Отладчик при работе в Mixed mode (тот самый случай тестирования С++ кода в MSTest) ведет себя неортодоксально. Норовит подвиснуть почем зря.
  • Зачем-то сломали поведение “Project dependencies”. Теперь, чтобы подсунуть линкеру другую статическую либу из этого же солюшена, нужно проект с библиотекой добавить в рефересы проекта. Совсем как в C# проектах. Немного неожидано. Зависимости проекта оставлены лишь для определения порядка сборки.

Технологии – вперде!

Интересно, что в C# настоятельно не рекомендуется вызывать виртуальные методы из конструкторов. По той же, в общем-то, причине, что и в C++.

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

Программерское: Мой топ признаков плохого кода

Собрал тут свой собственный список признаков факапнутости кода.

На первом месте – классы со словом “Manager” в названии. Безоговорочный лидер в номинации “превратим любой код в страшные спагетти”. Самое ужасное в таких классах это то, что делаются они из самых лучших побуждений. Впрочем, так всегда – самые кошмарные деяния совершаются под прикрытием лозунга “мы же хотим тебе лучшего”. Я специально не пишу о внутренностях этих классов, само название “Manager” для меня уже достаточное основание к тому, чтобы код полетел в помойку.

Второе место достается небезопасным преобразованиям типов. Ну, это когда в коде появляется преобразование от класса-предка к классу-потомку. Вроде этого

class A;
class B: public A;

void DoSomething(A* ptr)
{
(B*)ptr->MethodOfB();
}

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

Третье место – классы “универсальный всемогутор”.  Близкий родственник класса “Manager”, протащеный оным на ключевую позицию в проекте, способен отравлять жизнь двум-трем поколениям программистов, которым выпадет этот код поддерживать. Обычно характеризуются тучей публичных методов, никак не связанных по смыслу ни друг с другом, ни с названием и предназначением класса и не изменяющих и не использующих ни одного из свойств класса, но при этом зачастую имеющих тучу неявных связей друг с другом.

Четвертое место – преднамеренное нарушение энкапсуляции. Это когда в класс “бутылка пива” пытаются заодно вкрячить нечто, что вроде как должно научить экземпляры его автоматически упорядочиваться внутри класса “ящик пива”. Когда через неделю закономерно выяснится, что бутылки нужно упорядочивать в сикспаки, начинается любимый в народе аттракцион “аврал”. Есть вообще только один случай, когда объектам допустимо иметь какие-то знания о контейнере, в которые их будут складывать – это преднамеренно делается интрузивный контейнер. Во всех остальных случаях такой код – индикатор того, что code review можно прекращать прямо сейчас, иначе проблемы этот код начнет доставлять уже на следующей неделе. В общем случае – запихивание в классы внешних нерелевантных зависимостей есть большое зло.

Пятый признак – преднамеренное игнорирование или извращенное употребление паттернов программирования. Когда из-за страха перед великим и ужасным шаблоном “фабрика” образюутся иерархии, где один-единственный параметр конфигурации спускается каждым промежуточным классом вниз на десять уровней, чтобы там ему где-то делали if или switch..case, когда при программировании на C++ боятся использовать RAII или бьют по рукам за bind; или же, наоборот, пихают повсюду smart pointers (доморощенные, конечно же, бо boost – это слишком сложно), жди беды. Очень быстро кто-то посередине той иерархии забудет, зачем нужен этот мистический параметр, нигде в обозримых окрестностях не применяющийсяи “пофиксит” код, обеспечив остальных неделями отладки (ведь юнит-тесты для такого кода писать не принято – он же очень сложный!); неиспользование RAII рождает трудноуловимые дедлоки, а умные поинтеры дают вкусить циклических зависимостей по полной.

Шестой признак – использование наследования там, где нужна агрегация. Например, есть интерфейс классов, есть базовый класс, которые реализует 85% всей требуемой функциональности. Позднее выясняется, что функциональность реализована вообще на 100%, а единственное требуемое различие – это разное поведение в случае создания класса с определенными внешними параметрами. Например, в реальной жизни есть у нас автомобиль. Точнее, его кузов. Во всем мире все производители просто ставят разные двигатели на один и тот же кузов и обзывют это обидным словом “комплектация”. В мире программирования часто происходит закат разума за ум – вместо того, чтобы создать класс “автомобиль” со свойством “двигатель”, начинают городить иерархию “автомобиль А с двигателем Б”, автомобиль Б с двигателем А” и так далее, что вообще противоречит здравому смыслу. Еще бы, класс автомобиль оказывается наследником класса “двигатель”. Проблема тут в том, что разобраться во всем этом хитросплетении и потом поддерживать ее становится решительно невозможно. В то же время правильный вариант – создать нужный объект тпиа “двигатель” и передать его новому объекту типа “автомобиль” при инициализации- не только сэкономил бы пару тысяч строк кода, но и позволил бы унифицировать и протестировать все имеющиеся автомобили и двигатели отдельно друг от друга и даже попробовать впоследствии легко примерить движок от Мерседеса на Запорожец.

Пятисекундка ненависти

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

Юнит-тестирование. Всем чтить!

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

Но дело не в этом. Искал иформацию о куске говна, а нашел бриллиант. Отличная, замечательная презентация о юнит-тестах.  Здесь.

ППКС.

Код: до и после н.э. Зачем на самом деле нужны юнит-тесты.

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

БОльшая половина кода нашего проекта на первый взгляд выглядит очень хардкорно. Если опустить пару моментов, где я вдоволь оттянулся с белой и черной шаблонной магией, то бросится в глаза, что остальная часть этой половины идет вразрез с заветами секты “ООП”  и ее течения “многоэтажные иерархии классов” (да, я редко сажаю леса вроде приведенного на картинке ниже). В глаза бросится, да не долетит, а упадет и стечет (может быть даже прямо в ботинки).

Многоэтажная иерархия
При ближайшем рассмотрении

Deadlock for free или почему нельзя делать suspend потокам

Сегодня коллега напоролся на дамп программы, которая зависла. Такое бывает, но когда затык происходит на int* pInt = new int, Штирлицу положено насторожиться.

Налицо deadlock, но прикол в том, что в коде никакой синхронизации в принципе нет вообще. Зато была приаттачена куча всяких bounds-checker’ов вкупе с отладчиками, что заставило меня вспомнить, что я когда-то где-то читал о том, что  неосторожное обращение с менеджером памяти может привести к дедлоку.

На самом деле, неосторожно обратиться с менеджером памяти достаточно сложно, но, как это обычно бывает, компьютер сделает все, что Вы ему скажете, но никто не обещает, что это будет то, что имелось в виду.

Так вот, интернет уже неоднократно объяснил, что делать suspend потоку извне – очень, очень плохая идея. Есть миллиард объяснений, почему, но все они сводятся к общему знаменателю – поток сам распоряжается своими ресурсами и поэтому должен сам контроллировать свое поведение. Даже если на первый взгляд поток не использует никаких разделяемых ресурсов, при более пристальном рассмотрении это может оказаться совсем не так.

Любой поток делит как минимум один ресурс с остальными потоками. Оперативную память. Распределение памяти контроллирует менеджер памяти, который, как правило, общий для всех потоков, в определенных рамках, конечно. Соответственно, есть очень неплохие шансы, что при процедура выделения памяти синхронизирована внутри менеджера памяти, грубо говоря, менеджер имеет mutex внутри. А дальше все просто – Вася запустил поток, поток радостно поскакал по инструкциями и весело побежал за оперативкой. Менеджер памяти залочил было мютекс, а тут Вася бац! Suspend потоку! И тут же в другом потоке начинает ломиться к тому же самому менеджеру памяти, которому пофиг, ибо мютекс зажат другим потоком. И, заметьте, совершенно бесплатно приходит им всем, включая Васю, первый, второй поток и менеджер памяти злой дед лок.

Конечно, это довольно грубая картина и в реальности все может быть несколько иначе, но суть от этого не меняется – приостановка потока извне – верный способ рано или поздно нарваться на deadlock.

10 visitors online now
10 guests, 0 members
Max visitors today: 15 at 09:10 am MST
This month: 23 at 05-03-2012 08:23 am MST
This year: 29 at 01-23-2012 02:50 am MST
All time: 45 at 02-23-2011 09:11 am MST