?

Log in

No account? Create an account
dump -0f - /dev/mind
Я знаю Haskell, OCaml, GSM, эндофункторы и много других страшных слов
Что лучше: два кило по кило, и кило по пол-кило или просто три кило? 
9th-Jan-2007 08:47 pm
Новый Год, вагон работы и подкравшийся от сотрудников простудифилис привели к тому, что я еще даже на прошлогодние комментарии не поотвечал. Как говориться "сколько всего не сделано, а ведь сколько еще предстоит не сделать!".

Непорядок. Надо постепенно исправляются. Начну с баечки, которая давным-давно 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 накопившихся комментариев
Comments 
9th-Jan-2007 06:55 pm (UTC)
У нас в коде была функция Lamerize() которая переводила регистр набраного логина и пароля в строчные буквы, так как процент заявок на неработающие пароль по этой причине был очень высок :)
9th-Jan-2007 07:26 pm (UTC)
Хахахаха =))))
9th-Jan-2007 08:10 pm (UTC)
еще и трим() туда добавьте :)
один человек долго морочил голову, мол не подходит логин/пароль, а потом оказалось, что он вводил правильный логин, но _центрировал_ его пробелами, чтобы красиво было :)
9th-Jan-2007 07:07 pm (UTC)
а я в юности с перепуга на ассемблере под ДОС написал графический движок, типа виндуса. Очень виндус хотелось, но двушка не тянула ;-) Точнее, не виндусь хотелось, а красот графических, типа там менюшков стандартно-серо-объёмных, окошечков и кнопочков, чтоб мышой давить. Использовал в как составную часть дипломного проекта, на демнострационной институтской четвёрке тоже работало... Ех, где мои 21 лет(а,ов) ;-)

А сейчас вот, к старости, всё в консоль тянет, в юниксовую. Анекдот-ру почитать в lynx'е, судоку текстовую погадать...
9th-Jan-2007 09:49 pm (UTC)
лучше в elinks'е - там и табы, и прогрессбар и цвета есть :)
(Deleted comment)
9th-Jan-2007 09:45 pm (UTC)
...клиппер...
9th-Jan-2007 10:57 pm (UTC)
Нееееет, FoxPro 2.6 было хуже ;)
9th-Jan-2007 11:28 pm (UTC)
FrameWorks II и FrameWorks III.

FoxPro не был хуже - он не тормозил так феерично как это делал клиппер.
10th-Jan-2007 01:17 am (UTC)
clipper summer 87 - был всё таки хуже фокса
10th-Jan-2007 08:55 am (UTC)
9th-Jan-2007 07:31 pm (UTC)
Либо я уже туплю под вечер, либо запись point.y << 16 + point.x сделает не совсем то, что ты ожидаешь. А именно сделает сдвиг на (16 + point.x) разрядов, ибо у сложения приоритет выше, чем у сдвига.

(point.y << 16) + point.x?

Вполне вероятно, что у тебя просто переполнение целочисленной переменной имело место быть, отсюда и отрицательные числа
9th-Jan-2007 07:52 pm (UTC)
Надо было мне делать copy-paste кода as-is и не выделываться, благо исходники сохранились. Да, это я тупею - скобки там были.
10th-Jan-2007 07:40 am (UTC)
А почему вы не пользовались готовыми макросами типа MAKELONG/MAKEPOINT? 8)
Они, вроде как, именно для твоей ситуации и были разработаны, с учётом преобразования типов и сдвигов.
Хотя, все хороши задним умом ;)
10th-Jan-2007 08:00 am (UTC)
Как-то извилины не поднялись использовать(или) макросы для выполнения операции, суть которой сводится к одной инструкции процессора. Ведь вроде ж на С++ писали, как-никак гибридный язык...
10th-Jan-2007 08:16 am (UTC)
Ага, и, если верить МелкоСофту, Windows -- тоже объектно-ориентированная система ;)
Хотя, реально объекты я там увидел значительно позже, когда появился COM.
Правда и структуры, по большому счёту, тоже можно считать объектами, но это уже другая история...
Эх, навеяли вы мне воспоминания о своём "бурном прошлом" ;) с 5.25" дискетами (до сих пор где-то пачка с "полезным софтом" валяется) и программированием в 4 руки...
10th-Jan-2007 08:36 am (UTC)
Но-но, pair programming это не "бурное прошлое", а спонтанно открытая незамутненными древними программистами(нами) современная эффективная техника разработки. Эдакий методологический Стоунхендж. :)
10th-Jan-2007 09:29 am (UTC)
Я думаю, что такая техника могла быть открыта разными группами незамутнённых древних программистов независимо друг от друга, т.к. и-нета, как было сказано выше, не было, а флоппи-нет был не очень удобен для обмена опытом ;) А фидо-сисопки чаще сводились к просто приятному пивовремяпровождению ;)
А времена моего "бурного прошлого" просто совпали во временной шкале с "изобретением pair programming" ;)
9th-Jan-2007 08:20 pm (UTC)
Был случай - в результате неких арифметических действий получалось число, которое в отладчике отображалось как -0.0е234 (за экспоненту не ручаюсь), и при возведении его в квадрат программа падала от переполнения :)
9th-Jan-2007 08:31 pm (UTC)
А мой школьный калькулятор исправно писал ERR при попытке взять корень из -0 :)
9th-Jan-2007 10:45 pm (UTC)
С самым загадочным багом лично я столкнулся при отладке Frenzy 1.0-BETA - на некоторых компах привод не опознавал записанный диск как загрузочный, в то время как на других компьютерах все было ОК. Предыдущая версия 0.3 грузилась везде. Замечу, что проблема возникала на разных компьютерах, т.е. была 100% повторяема.

После пошагового выяснения различий между версиями проблема была найдена. Оказывается, во всем было виновато... количество папок в корне диска. В 1.0-BETA я сделал всего две папки (boot и frenzy), а в 0.3 их было три (boot, frenzy, readme). Стоило только добавить в образ третью пустую папку, и глюк исчез. Проверяли неоднократно - да, так и есть.

В чем конкретно причина такого загадочного поведения, я докапываться не стал. Может, mkisofs так выделывается, может с самими приводами что-то не так...
10th-Jan-2007 01:28 pm (UTC)
Я встречал в сети документ, подробно разъясняющий El-torito, и там было указано на типичные ошибки при создании бут-дисков... Что-то подобное там упоминалось.. Т.е. этос корее всего таки бага в mkisofs
9th-Jan-2007 11:54 pm (UTC)
встречался с подобным раз миллион.
даже рефлекс появился — если что-то не работает, как нужно, делать clean rebuild, еще до того, как лезть в отладчик.
10th-Jan-2007 06:41 am (UTC)
Когда я сталкивался с подобным, брал дизасемблированный кусок кода и пытался понять чего она выделывается. Но чаще, конечно, make clean ; make
спасал
10th-Jan-2007 12:48 pm (UTC)
В стандартном классе CString есть метод
int Find( TCHAR ch, int nStart )

Он, ясен пень, ищет первое вхождение символа в строку, начиная поиск с позиции nString. Но при этом:

nStart

номер символа в строке, с которого необходимо начинать поиск, или 0 чтобы искать с начала строки. Символ в позиции nStart исключается из поиска если nStart не равен 0.


Внимание, вопрос: как искать с позиции номер 1?

И еще - зачем так сделано? У меня есть определенные идеи (последовательный поиск разделителей в строке), но они не оправдывают такого извращения.
19th-Jan-2007 11:33 pm (UTC) - Malx
Anonymous
А так ли это?
В том смысле что в доке то оно так... но на самом деле:

CString ttt;
ttt=L"012345";
int ap=ttt.Find(L"1",1);

выдает 1 :)
20th-Jan-2007 01:22 pm (UTC)
((unsigned long)point.x)<<16.
This page was loaded Oct 22nd 2019, 9:35 am GMT.