Как стать автором
Обновить

Комментарии 52

Неужели с помощью sed и awk?
Забыл хаб «Блог компании sed и awk» добавить, а больше не редактируется :-(
Про 64-битную миграцию еще может быть полезно почитать эппловский 64-Bit Transition Guide.
Вопрос может быть не совсем по теме:
Зачем переводят на 64 бита мелкие утилитки. типа WinMTR?
Какой профит от использования 64-битных версий? Они, ведь, даже в страшном кошмаре не должны жрать больше 4Гб памяти.
К большим проектам вопросов нет.
Вы же не считаете, что софт переписывают под 64, чтобы он смог больше памяти сожрать?)
Системные утилиты часто просто необходимо сделать 64-битными, чтобы они были полноценными. Например, FAR.
Почему? Вызовы системных функций проще? Или что еще?
Из-за подсистемы WoW64. Если 32-битный FAR запустить, то для него эмулируются 64-битные папки. А для 64-битного FAR файловая структура будет такой, какой и должна быть.
32-битный фар с незапамятных времен умеет заходить в неэмулируемый System32. А 32-битный плагин RegEdit под него — в 64-битный реестр.
Ещё 32-битные процессы не умеют полноценно работать с 64-битными процессами через хендл (но не наоборот).
Почему нельзя обойтись без ifdef'a?
typedef ptrdiff_t MyLong;
typedef size_t MyULong;
Потому что в 32-битном вариант MyLong является long. Это особенность конкретного проекта, которую изменить нельзя в силу большой кодовой базы.
Они имеют одинаковый размер и знаковость. В чем разница?
size_t — это unsigned int. Естественно unsigned long и unsigned int это разные типы.
Просто так взять и заменить нельзя. Когда проект такого размера, есть масса нюансов, которые вот так просто нельзя взять и победить. Одним словом, так было надо. :)
Кстати, интересно, а насколько давно все эти memsize типы появились?
Почему ими так неаккуратно пользуются, хотя си уже за 40 лет, а плюсам 30 с небольшим.
Как давно появились собственно и не важно. Дело в том, что буквально до последнего времени про это никто даже не думал. В книжках выпущенных всего несколько лет назад фигурирует грубо говоря for (int i =… ). И только заглядывая в книги последних лет, я начал видеть там size_t. Соответственно эти новые веяния программисты начинают брать на вооружение только сейчас. И дай бог лет через 5-10 будут чаще писать size_t, чем, например ulong. Пока до этого далеко.
Но ведь в api же наверняка правильные типы были. И разве при портировании с 16 бит на 32 разработчики не столкнулись с тем же ворохом проблем?
Программист идеальный и программист реальный — это два мало похожих человека :). А по поводу миграции 16 на 32 бита, да, были схожие проблемы. НО!
  • В проектах тех лет было на пару порядков меньше кода. И его можно было быстро просмотреть и поправить.
  • 32-битынй Int вместил в себя указатель и максимальный размер массива, в отличие от нынешней ситуации.

В результате, портирование шло во много раз проще.
То есть, нужные выводы не были сделаны потому, что всё само собой заработало?
Скорее все-таки большинство софта переписали тогда просто. А сейчас переписывать было не резон.
Вроде, если не изменяет память под 16-битными int и был 16 бит. На 32 — стал 32, вот и не парились. А есть еще такая замечательная вещь как short long =)
Эта проблема была только на PC/DOS.
Остальные платформы, основанные на 68k, MIPS, Sparc и т.д. 32-битные изначально.
А вы могли бы описать общее качество кода? Сколько примерно из 9млн строк можно отнести к категории говнокода.
На месте заказчика я бы внес запрет отвечать на подобные вопросы в NDA )
Общее качество кода — обычный код. В котором видна история. Вот эти модули писались, когда вышла MFC. Вот эти — когда COM появился. Как кольца на стволе дерева.

P.S. Мне не очень нравится термин «говнокод». Если код приносит миллионы долларов, то называть его говнокодом язык не поворачивается.
Сомнительная логика. Многие программисты предпочитают рассматривать своё занятие если не как искусство, то как утончённое ремесло. В таких вещах качество не тождественно окупаемости.
Только непонятно как это отношение относится к окупаемости и успешности продукта.
У меня был проект 40 Mb кода, перенес за полгода, причем немалую часть кода приходилось откатывать по причине активных сабмитов других разработчиков, на меня тоже нехорошо косились по той же самой причине.

Кстати неупомянуты unit-tests, неужели перевод делали на авось?

Поскольку переводил еще с VC6 была проблема с time_t

Решал ее так:

struct time64_t
{
time_t time;
time64_t( const time_t &tm )
{
time = tm;
}
operator time_t &()
{
return this->time;
}
operator void *()
{
return (void *)this->time;
}
time64_t(): time(0){};
};

struct timeb_rm // always has size 12 bytes
{
__int32 time;
unsigned __int16 millitm;
__int16 timezone;
__int16 dstflag;
timeb_rm(): time(0), millitm(0), timezone(0), dstflag(0) {}
timeb_rm( const _timeb &tm )
{
time = __int32(tm.time);
millitm = tm.millitm;
timezone = tm.timezone;
dstflag = tm.dstflag;
}
operator const _timeb ()
{
_timeb tm;
tm.time = time_t(time);
tm.millitm = millitm;
tm.timezone = timezone;
tm.dstflag = dstflag;
return tm;
}
};

Потому что правил по рабочему коду с сравнивал функциональность с VC6 и VC2005

дисклеймер: тэги CPP не сработали
Тесты были, и тестов было очень много. Без них вообще бы невозможно было.
А к чему сводится алгоритм обратного портирования, 64 на 32?
1. Создать машину времени.
2. Слетать в будущее, взять там 64-битный проект.
3. Вернуться в настоящее, просто запустить его на 32 битах.

А если серьезно, то во время портирования есть такой этап, когда компилируется и 32-битная, и 64-битная версия. Как правило это остается навсегда и так, чтобы 32-битной версии не было — это довольно редкая ситуация.
Какой смысл в 2015 году продолжать поддерживать 32-битную версию приложения? Если ее выбросить, вдвое уменьшится время на сборку билдов и уйдет порядочная часть #ifdefов.
Клиенты… Которые пользуются 32-битной версией по разным причинам. И силком их перетащить не всегда разумно с точки зрения бизнеса.
Я не сталкивался с проблемами перехода 32 на 64 бита в своих маленьких кусках кода на C под линуксом.

Есть какая-то практическая разница между линуксом и виндовсом в плане этой миграции?
Есть разница при миграции маленьких кусков (которые можно за вечер просмотреть) и проекта на 300Mb.
меня удивило количество адресной арифметики в таком большом проекте. Выглядит как какое-то бизнес-приложение, зачем в нём шаманить с указателями?
К сожалению, ваша статья больше похожа на «мы с NDA делали NDA а там NDA потому что NDA».

Я понимаю, что вы сделали полезную штуку и заказчик наверное рад, но как статья вышло не очень. Даже с рекламной точки зрения для девелоперов вышло скучно. Возможно стоило детальнее рассказать про какой-то один кейс, который не под NDA.

Ну и, конечно, подобная мелочевка выглядит очень нелепо: под NDA закрывать даже то, почему нужна адресная арифметика. Может причины тому и есть, но всем вокруг на них откровенно наплевать.
Из того что раскрыто, видно, что в проекте используется обращение к WinAPI, в том числе прямая работа с оконными сообщениями. При обработке и посылке оконных сообщений часто приходится преобразовывать указатели в числа и обратно.
тут я совершенно не в теме, поэтому это мне непонятно.

В API получается хитрят и просят положить пойнтер в поле, которое шириной в 4 байта?
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
Некоторые сообщения принимают указатели, а сигнатура функции — одна. Если при преобразовании вместо WPARAM, написать более привычный DWORD, то будет ошибка на 64-битной системе.

Вот еще пример функции, где может происходить такое преобразование:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633584(v=vs.85).aspx
Как правило, эта функция используется, чтобы хранить в окне указатель на класс, который за это окно отвечает. Правильно вместо нее использовать GetWindowLongPtr, но ведь в 32 битной системе «и так работает».
т.е. проблема в том, что API дизайнилось с явными ошибками?
Явных ошибок нет, просто нужна аккуратность в его использовании.
аа, т.е. раньше работало хоть и с варнингами то, что работать не должно было?
Wait, wait, т.е. вместо того, чтобы взять fixed-size типы из stdint вы наоборот, меняли все на архитектурно-зависимые? Но зачем?
Указатель на одной платформе 4 байта, а на другой 8. И с этим ничего не поделаешь.
intptr_t?
Ну в статье так и написано. Только ptrdiff_t выбрали, как более знакомый людям.
Нет, в статье у вас сплошные #if defined
Более того, ptrdiff_t, внезапно, может отличаться от intptr_t (хоть в данном случае и не), не стоит использовать его просто потому, что «более знаком». Он нужен именно для хранения разницы указателей. Более того, внутри одного массива/структуры.
Статья — это микроскопические щели, через которые видны отдельные элементы слона, такие как соломинка, прилипшая к туловищу, один глаз, и испачканный бивень :). Не стоит по этим элементам судить о слоне целиком. То, что в статью попали #ifdef и объявления типов, ничего не значит и не стоит их обсуждать. Если бы в статье было написано про тип time_t и проблемы с ним связанные, то, казалось бы, что в основном мы работали именно с этим. Это не так. Если бы написали про правки дистрибутива, казалось, бы что именно это сложно и важно. Это не так.

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

По поводу #ifdef спешу успокоить. Мы максимально старательно их избегали. Но не всегда это возможно. Просто в статью попало упоминание #ifdef. Могло бы попасть что-то ещё и казаться важным.
А про legacy 16 и 8-бит код напишете? *серьёзно, не шучу* Часто такой код имеет вставки Ассемблера.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий