Dmitry Astapov (_adept_) wrote,
Dmitry Astapov
_adept_

Categories:

Что лучше: два кило по кило, и кило по пол-кило или просто три кило?

Новый Год, вагон работы и подкравшийся от сотрудников простудифилис привели к тому, что я еще даже на прошлогодние комментарии не поотвечал. Как говориться "сколько всего не сделано, а ведь сколько еще предстоит не сделать!".

Непорядок. Надо постепенно исправляются. Начну с баечки, которая давным-давно overdue. Итак, страшная история про "<<8<<8".

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

Это сейчас можно провести пол-часа в обнимку с Гуглом и найти/честно стянуть готовое. А в то благословенное время компьютеры были большими, модемы - на 2400, интернета не было, а софт был на дискетках 5.25 (прочие душещипательные детали можно почерпнуть из вот этого поста).

Я не буду рассказывать ужасы о том, как были сформулированы требования к софту и каким образом был были зафиксированы договорные отношения между Заказчиком и Исполнителями, т.к. это пошло и не ново. Просто представьте себе обычный project from hell, без требований, без обязательств, с непонятными сроками и с неопытными исполнителями, жизнерадостно лабающими в четыре руки на одном компьютере код на, кажется, Visual C++ 5.0. Представьте и попробуйте поставить себя на место студентов-исполнителей.

Код пишется вечерами, почти без проектирования. Coding sessions прерываются распитием пива, игрой в "UFO: Enemy unknown" и выполнением лабораторных работ. Код или пишется со скоростью 20К в сутки, или не пишется вообще никак. И в один из таких "медленных" дней мы сталкиваемся с тем, что нам надо узнать, в каком конкретно месте в пределах List Control-а пользователь сдела щелчок мышкой. Зачем - я уже и не припомню. Но точно помню, что нужно было позарез.

А вот фигушки! Стандартный MFC-ный ListCtrl обрабатывал событие "button click" и "button double click" внутри себя, не отдавая его дальше и не инкапсулируя мышиное событие в порождаемый event "list item selected". Битый час мы крутили ListCtrl и так и эдак - нет, не выходит каменный цветок. Постепенно приходит понимание того, что надо делать Костыль.

Сказано - сделано. На свет появляется widget с названием, стыдно сказать, FuckingListCtrl. Он отнаследован от стандартного ListCtrl и отличается от предка только методом "OnLButtonDown". Наша реализация уведомляет контейнер, в котором находится список, о нажатии на кнопку, причем делает это вот так:

void CFuckingListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
GetParent()->SendMessage(WM_LBUTTONDOWN, nFlags, (point.y<<16)+point.x);
CListCtrl::OnLButtonDown(nFlags, point);
}


Т.е. берем координаты мышиного клика, "упаковываем" в один WORD, засовываем в событие WM_LBUTTONDOWN и вуаля! Казалось бы, костыль себе и костыль. Собираем, запускаем - не работает. Точнее, работает, но как-то странно - отладочная печать в контейнере показывает, что в событии WM_LBUTTONDOWN приезжают ... отрицательные координаты.

Запускаем несколько раз, стараясь не двигать мышку. Координаты отрицательные и каждый раз разные. Хм. Запускаем под отладчиком, ставим breakpoint-ы. В CFuckingListCtrl координаты нормальные, адекватные, положительные. На следующем breakpoint-е (в контейнере) они неадекватные и отрицательные.

Чешем репу, суем отладочный вывод куда угодно. Проходит два часа. Аномалия никуда не уходит.

В какой-то момент запускаем код под отладчиком, проходим пошагово, вбив в watchlist значения "point.x" и "point.y". Аномалия по-прежнему имеет место. Добавляем в watchlist "(point.y<<16)+point.x" . Очень странно. Получается, что именно тут и вылазит фигня, так как значение этого выражения получается странным и отрицательным. Тут же вбиваем в "Debug->Evaluate" это же выражение, в которое подставлены реальные значения координат. Все вычисляется правильно.

Забавно. Получается, с константами "сдвиг влево" считается правильно, а с переменными - абы как. Да ну, фигня какая! Не может такого быть. Начинаем вбивать в evaluate выражения "point.x<<2", "point.x<<3" и т.п. Выясняется, что сдвиг на 2,3,4,5,6,7,8 - работает нормально, а дальше - фиг.

Переписываем код вот так: "((point.y<<8)<<8)+point.x".

Все работает. Немая сцена.

Чтобы проверить, что проблема действительно была в этом, меняем обратно на "<<16" и пересобираем. Глючит. Меняем на "<<8<<8". Работает.

Бл#! @#$%$#! @#$#@#%#! Все равно не верится. Еще раз меняем на "<<16" и случайно вместо "rebuild" давим на "clean rebuild". Все долго-долго пересобирается с нуля и ... работает.

Причем работает под отладчиком, без отладчика, в сборке "Debug", в сборке "Release" - как угодно.

Следующие пол-часа мы могли только материться, менять "<<16" на "<<8<<8" и обратно, пересобирать код в разных позах и пытаться сделать так, чтобы баг проявился еще раз. Ни-фи-га.

В конце-концов мы оставили в коде "<<8<<8". Просто так, на всякий случай.

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

Disclaimer: данный пост - не о том, как (не надо) писать программы с использованием MFC. Не судите строго :)

а теперь попробую ответить на 87 накопившихся комментариев
Tags: баечки
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 26 comments