Новый Год, вагон работы и подкравшийся от сотрудников простудифилис привели к тому, что я еще даже на прошлогодние комментарии не поотвечал. Как говориться "сколько всего не сделано, а ведь сколько еще предстоит не сделать!".
Непорядок. Надо постепенно исправляются. Начну с баечки, которая давным-давно 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 накопившихся комментариев