Новый Год, вагон работы и подкравшийся от сотрудников простудифилис привели к тому, что я еще даже на прошлогодние комментарии не поотвечал. Как говориться "сколько всего не сделано, а ведь сколько еще предстоит не сделать!".
Непорядок. Надо постепенно исправляются. Начну с баечки, которая давным-давно 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". Наша реализация уведомляет контейнер, в котором находится список, о нажатии на кнопку, причем делает это вот так:
Т.е. берем координаты мышиного клика, "упаковываем" в один 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 накопившихся комментариев
Непорядок. Надо постепенно исправляются. Начну с баечки, которая давным-давно overdue. Итак, страшная история про "<<8<<8".
Было это в далеком 19xx году, когда я учился не то в 11-ом классе, не то на первом курсе. Как-то совершенно неожиданно нам с
Это сейчас можно провести пол-часа в обнимку с Гуглом и найти/честно стянуть готовое. А в то благословенное время компьютеры были большими, модемы - на 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 накопившихся комментариев
(no subject)
Date: 2007-01-09 06:55 pm (UTC)(no subject)
Date: 2007-01-09 07:26 pm (UTC)(no subject)
Date: 2007-01-09 08:10 pm (UTC)один человек долго морочил голову, мол не подходит логин/пароль, а потом оказалось, что он вводил правильный логин, но _центрировал_ его пробелами, чтобы красиво было :)
(no subject)
Date: 2007-01-09 07:07 pm (UTC)А сейчас вот, к старости, всё в консоль тянет, в юниксовую. Анекдот-ру почитать в lynx'е, судоку текстовую погадать...
(no subject)
Date: 2007-01-09 09:49 pm (UTC)(no subject)
Date: 2007-01-09 07:09 pm (UTC)честно сказать, давно не являюсь "разработчиком", но до сих пор с содроганием вспоминаю отлов глюков и багов в те времена, когда им являлся.
хуже всего был клиппер...
(no subject)
Date: 2007-01-09 09:45 pm (UTC)(no subject)
Date: 2007-01-09 10:57 pm (UTC)(no subject)
Date: 2007-01-09 11:28 pm (UTC)FoxPro не был хуже - он не тормозил так феерично как это делал клиппер.
(no subject)
Date: 2007-01-10 01:17 am (UTC)(no subject)
Date: 2007-01-10 08:55 am (UTC)(no subject)
Date: 2007-01-09 07:31 pm (UTC)(point.y << 16) + point.x?
Вполне вероятно, что у тебя просто переполнение целочисленной переменной имело место быть, отсюда и отрицательные числа
(no subject)
Date: 2007-01-09 07:52 pm (UTC)(no subject)
Date: 2007-01-10 07:40 am (UTC)Они, вроде как, именно для твоей ситуации и были разработаны, с учётом преобразования типов и сдвигов.
Хотя, все хороши задним умом ;)
(no subject)
Date: 2007-01-10 08:00 am (UTC)(no subject)
Date: 2007-01-10 08:16 am (UTC)Хотя, реально объекты я там увидел значительно позже, когда появился COM.
Правда и структуры, по большому счёту, тоже можно считать объектами, но это уже другая история...
Эх, навеяли вы мне воспоминания о своём "бурном прошлом" ;) с 5.25" дискетами (до сих пор где-то пачка с "полезным софтом" валяется) и программированием в 4 руки...
(no subject)
Date: 2007-01-10 08:36 am (UTC)(no subject)
Date: 2007-01-10 09:29 am (UTC)пивовремяпровождению ;)А времена моего "бурного прошлого" просто совпали во временной шкале с "изобретением pair programming" ;)
(no subject)
Date: 2007-01-09 08:20 pm (UTC)(no subject)
Date: 2007-01-09 08:31 pm (UTC)(no subject)
Date: 2007-01-09 10:45 pm (UTC)После пошагового выяснения различий между версиями проблема была найдена. Оказывается, во всем было виновато... количество папок в корне диска. В 1.0-BETA я сделал всего две папки (boot и frenzy), а в 0.3 их было три (boot, frenzy, readme). Стоило только добавить в образ третью пустую папку, и глюк исчез. Проверяли неоднократно - да, так и есть.
В чем конкретно причина такого загадочного поведения, я докапываться не стал. Может, mkisofs так выделывается, может с самими приводами что-то не так...
(no subject)
Date: 2007-01-10 01:28 pm (UTC)(no subject)
Date: 2007-01-09 11:54 pm (UTC)даже рефлекс появился — если что-то не работает, как нужно, делать clean rebuild, еще до того, как лезть в отладчик.
(no subject)
Date: 2007-01-10 06:41 am (UTC)спасал
(no subject)
Date: 2007-01-10 12:48 pm (UTC)int Find( TCHAR ch, int nStart )
Он, ясен пень, ищет первое вхождение символа в строку, начиная поиск с позиции nString. Но при этом:
nStart
номер символа в строке, с которого необходимо начинать поиск, или 0 чтобы искать с начала строки. Символ в позиции nStart исключается из поиска если nStart не равен 0.
Внимание, вопрос: как искать с позиции номер 1?
И еще - зачем так сделано? У меня есть определенные идеи (последовательный поиск разделителей в строке), но они не оправдывают такого извращения.
Malx
Date: 2007-01-19 11:33 pm (UTC)В том смысле что в доке то оно так... но на самом деле:
CString ttt;
ttt=L"012345";
int ap=ttt.Find(L"1",1);
выдает 1 :)
(no subject)
Date: 2007-01-20 01:22 pm (UTC)