Недавно наша команда завершила миграцию на 64-битную платформу довольного большого проекта (9 млн строк кода, 300Mb исходников). Проект занял полтора года. Хотя в силу NDA мы не можем привести название проекта, надеемся, что наш опыт окажется полезен другим разработчикам.
Об авторах
Многие знают нас как авторов статического анализатора кода PVS-Studio. Это действительно основная наша деятельность. Однако кроме этого мы еще принимаем участие в сторонних проектах в качестве команды экспертов. Мы называем это «продажа экспертизы». Недавно мы публиковали отчет о работе над кодом Unreal Engine 4. Сегодня время очередного отчета о проделанной работе в рамках продажи нашей экспертизы.«Ага, значит дела с PVS-Studio у них идут совсем плохо!», — может воскликнуть читатель, который следит за нашей деятельностью. Спешим огорчить охочую до сенсаций публику. Участие в таких проектах очень важно для нашей команды, но совершенно по другой причине. Таким образом мы можем сами более активно пользоваться нашим анализатором кода в реальной жизни, нежели просто при его разработке. Реальное применение анализатора в коммерческих проектах, над которыми трудятся десятки или даже сотни программистов, дает потрясающий опыт команде PVS-Studio. Мы видим, как применяется наш инструмент, какие возникают сложности и что необходимо изменить или хотя бы просто улучшить в нашем продукте.
Поэтому мы и дальше планируем участвовать в таких проектах в рамках продажи нашей экспертизы. Пишите, если у вас есть подходящий для нас проект. А пока рады представить отчет о миграции кода на 64-битную платформу.
Введение, или в чем проблема? Рамки проекта и размер команды
На первый взгляд с темой миграции кода на платформу x64 все уже понятно. Проверенная временем статья "Коллекция примеров 64-битных ошибок в реальных программах" была написана нами в 2010 году. Наш курс "Уроки разработки 64-битных приложений на языке Си/Си++" — в 2012 году. Казалось бы, читай, делай как написано, и все будет хорошо. Зачем же понадобилось заказчику обращаться к сторонней организации (к нам), и почему даже мы потратили на проект полтора года? Ведь если мы делаем в рамках PVS-Studio анализ 64-битных проблем, то вроде бы должны разбираться в теме? Конечно мы разбираемся, и это была главная причина того, что заказчик обратился к нам. Но почему у заказчика вообще появилась мысль обратиться к кому-то по поводу 64-битной миграции?Давайте сначала опишем проект и заказчика. Так как NDA запрещает говорить напрямую, то приведем лишь количественные характеристики. Проекту, над которым мы работали, порядка 20 лет. В настоящее время над ним ежедневно работают несколько десятков разработчиков. Клиенты — крупные компании, продажи единичны, так как продукт очень нишевый и узкоспециализированный.
Ну и самое главное — это размер. 9 млн строк кода, 300Mb исходного кода, тысяча проектов в решении (.sln) это ОЧЕНЬ много. Платформа — Windows only. Но даже с таким проектом 64-битная миграция вроде бы должна быть понятной. Для того, чтобы перевести такой проект на x64 надо всего лишь:
- на несколько месяцев полностью остановить разработку;
- быстренько заменить типы данных на 64-битные;
- проверить, что все корректно работает после замены;
- можно возвращаться к разработке.
В силу бизнес-ограничений заказчик не мог остановить процесс разработки. Его клиентам постоянно нужны новые релизы, исправления проблем, специальные возможности и т.п. Остановить разработку в таких условиях означает остановить бизнес. Поэтому заказчик стал искать команду, которая может выполнить миграцию без остановки процесса разработки. Такой командой стали мы, так как наша компетенция в 64-битной разработке подтверждается анализатором кода PVS-Studio и статьями по данной тематике.
Мы выполнили миграцию за полтора года. С нашей стороны в проекте первые полгода принимали участие два человека, затем еще год уже четыре человека. Почему так долго? Первые полгода два человека занимались настройкой инфраструктуры, знакомством с проектом, проверкой конкретных способов миграции. Затем, через полгода, когда задача стала уже более конкретной, к проекту подключились еще люди и уже 4 человека за год выполнили миграцию.
Как перенести проект на 64-битную систему?
Перенос проекта на 64-битную платформу по большому счёту заключается в следующих двух шагах:- Создание 64-битной конфигурации, получение 64-битных версий сторонних библиотек и сборка проекта.
- Исправление кода, который приводит к ошибкам в 64-битной версии. Этот пункт почти полностью сводится к тому, что нужно заменить 32-битные типы на memsize-типы в коде программы.
Портирование большого и активно развивающегося проекта не должно мешать текущей разработке, поэтому мы предприняли следующие меры. Во-первых, мы делали все наши правки в отдельной ветке, чтобы не ломать основную сборку. Когда очередной набор наших изменений был готов и протестирован, мы объединяли наши изменения с основной веткой. А во-вторых, мы не стали менять жёстко 32-битные типы на memsize-типы. Мы ввели свои типы и делали замену на них. Это было сделано для того, чтобы избежать потенциальных проблем, таких как, например, вызов другой реализации перегруженной функции, а также, чтобы иметь возможность быстро откатить наши изменения. Типы были введены приблизительно таким образом:
#if defined(_M_IX86)
typedef long MyLong;
typedef unsigned long MyULong;
#elif defined(_M_X64)
typedef ptrdiff_t MyLong;
typedef size_t MyULong;
#else
#error "Unsupported build platform"
#endif
Хотим еще раз подчеркнуть, что мы меняли типы не на size_t/ptrdiff_t и им подобные типы, а на свои собственные типы данных. Это дало большую гибкость и возможность легко отследить те места, которые уже портированы, от тех, где пока «не ступала нога человека».
Возможные подходы к миграции: их плюсы и минусы, в чем мы ошиблись
Первая идея портирования проекта была следующей: сначала заменить все 32-битные типы на memsize-типы за исключением тех мест, где 32-битные типы было необходимо оставить (например, это структуры, представляющие собой форматы данных, функции, обрабатывающие такие структуры), а потом уже привести проект в рабочее состояние. Мы решили сделать так для того, чтобы сразу устранить как можно больше 64-битных ошибок и сделать это за один проход, а потом доправить все оставшиеся предупреждения компилятора и PVS-Studio. Хотя такой способ работает для небольших проектов, в нашем случае он не подошёл. Во-первых, замена типов занимала слишком много времени и приводила к большому количеству изменений. А во-вторых, как мы ни старались делать это аккуратно, мы, тем не менее, правили по ошибке структуры с форматами данных. В результате, когда мы закончили работать над первой частью проектов и запустили приложение, мы не смогли загрузить предустановленные шаблоны интерфейсов, так как они были бинарными.Итак, первый план предполагал следующую последовательность действий.
- Создание 64-битной конфигурации.
- Компиляция.
- Замена большинства 32-битных типов на 64-битные (вернее на memsize-типы).
- Линковка со сторонними библиотеками.
- Запуск приложения.
- Правка оставшихся предупреждений компилятора.
- Правка оставшихся 64-битных ошибок, которые выявит анализатор PVS-Studio.
Теперь мы решили сначала как можно быстрее получить рабочую 64-битную версию приложения, а потом уже поправить явные 64-битные ошибки. Наш план теперь исключал массовую замену типов и предполагал правку только явных 64-битных ошибок:
- Создание 64-битной конфигурации.
- Компиляция.
- Линковка со сторонними библиотеками.
- Запуск приложения.
- Правка предупреждений компилятора.
- Правка самых приоритетных 64-битных ошибок, которые выявит анализатор PVS-Studio.
Дальше нам предстояло поправить предупреждения компилятора и 64-битные предупреждения PVS-Studio, чтобы устранить найденные и потенциальные падения. Так как общее количество 64-битных предупреждений PVS-Studio исчислялась тысячами, то мы решили исправить только самые основные: неявные преобразования memsize-типов к 32-битным типам (V103, V107, V110), преобразования указателей к 32-битным типам и наоборот (V204, V205), подозрительные цепочки преобразований (V220, V221), приведение в соответствие типов в параметрах виртуальных функций (V301) и замена устаревших функций на новые версии (V303). Описание всех этих диагностик вы можете найти в документации.
Другими словами, задача этого этапа — исправить все 64-битные сообщения PVS-Studio только первого уровня (level 1). Это наиболее важные диагностики. И для запуска 64-битного приложения все ошибки 64 L1 должны быть исправлены.
Большинство таких правок сводилось к замене 32-битных типов на memsize-типы, как и при первом подходе. Но в этот раз, в отличие от первого подхода, эти замены носили выборочный и итеративный характер. Это было связано с тем, что правка типов параметров функции тянула за собой правки типов локальных переменных и возвращаемого значения, которые в свою очередь приводили к правке типов параметров других функций. И так до тех пор, пока процесс не сошёлся.
Ещё один минус этого подхода в сравнении с первым заключается в том, что мы таким образом поправили только основные 64-битные ошибки. Например, типы счётчиков циклов мы не правили. В подавляющем большинстве случаев это было не нужно. И это не приводит к ошибкам, но возможно где-то это нужно было сделать, и такие места мы пропустили и не найдём при нашем подходе. Другими словами, возможно со временем еще кое-что придется поправить.
При портировании приложения нам также потребовалось получить 64-битные версии сторонних библиотек. В случае библиотек с открытым исходным кодом мы старались собрать их из тех же исходников, из которых были собраны 32-битные версии. Это было связано с тем, что мы хотели сохранить возможные правки в коде сторонних библиотек, если такие были, и также нам нужно было собирать их по возможности в такой же конфигурации, в какой они были собраны для 32-битной версии. Например, некоторые библиотеки были собраны с настройкой не считать wchar_t встроенным типом или с отключенной поддержкой юникода. В таких случаях нам пришлось немного повозиться с параметрами сборки, прежде чем мы смогли понять, почему наш проект не может слинковаться с ними. Какие-то библиотеки не предполагали сборку под 64-битную версию. И в этом случае нам приходилось либо самим конвертировать их, либо скачивать более новую версию с возможностью сборки под 64-битную платформу. В случае коммерческих библиотек мы либо просили докупить 64-битную версию, либо искали замену не поддерживающимся больше библиотекам, как в случае с xaudio.
Также нам нужно было избавиться от всех ассемблерных вставок, так как в 64-битной версии компилятора Visual C++ ассемблер не поддерживается. В этом случае мы либо использовали intrinsic функции там, где это можно было сделать, либо переписывали код на C++. В некоторых случаях это не приводило к ухудшению производительности, например, если в 32-битном ассемблерном коде использовались 64-битные MMX регистры, то в 64-битной версии у нас и так все регистры 64-битные.
Сколько времени нужно, чтобы исправить 64-битные ошибки в таком проекте
В начале работы над большим проектом сложно сказать, сколько времени займёт портирование. Существенное время на первом этапе у нас заняла сборка сторонних библиотек, настройка окружения для ежедневной сборки 64-битной версии и прогона тестов. Когда работа над первой частью проектов была закончена, мы смогли оценить, с какой скоростью мы работаем, по объёму портированного кода за определённый период.Примеры 64-битных проблем, с которыми мы столкнулись
Самой распространённой ошибкой при портировании на 64-битную платформу было явное приведение указателей к 32-битным типам, например, к DWORD. В таких случаях решением была замена на memsize-тип. Пример кода:MMRESULT m_tmScroll = timeSetEvent(
GetScrollDelay(), TIMERRESOLUTION, TimerProc,
(DWORD)this, TIME_CALLBACK_FUNCTION);
Также проявились ошибки при изменении параметров виртуальных функций в базовом классе. Например, у CWnd::OnTimer(UINT_PTR nIDEvent) тип параметра поменялся с UINT на UINT_PTR с появлением 64-битной версии Windows, и соответственно во всех наследниках в нашем проекте нам тоже надо было выполнить эту замену. Пример кода:
class CConversionDlg : public CDialog {
...
public:
afx_msg void OnTimer(UINT nIDEvent);
...
}
Некоторые WinAPI фукции поддерживают работу с большими объёмами данных, как, например, CreateFileMapping и MapViewOfFile. И мы адаптировали код соответствующим образом:
Было:
sharedMemory_ = ::CreateFileMapping(
INVALID_HANDLE_VALUE, // specify shared memory file
pSecurityAttributes, //NULL, // security attributes
PAGE_READWRITE, // sharing
NULL, // high-order DWORD of the file size
sharedMemorySize, // low-order DWORD of the file size
sharedMemoryName_.c_str());
Стало:
#if defined(_M_IX86)
DWORD sharedMemorySizeHigh = 0;
DWORD sharedMemorySizeLow = sharedMemorySize;
#elif defined(_M_X64)
ULARGE_INTEGER converter;
converter.QuadPart = sharedMemorySize;
DWORD sharedMemorySizeHigh = converter.HighPart;
DWORD sharedMemorySizeLow = converter.LowPart;
#else
#error "Unsuported build platform"
#endif
sharedMemory_ = ::CreateFileMapping(
INVALID_HANDLE_VALUE, // specify shared memory file
pSecurityAttributes, //NULL, // security attributes
PAGE_READWRITE, // sharing
sharedMemorySizeHigh, // high-order DWORD of the file size
sharedMemorySizeLow, // low-order DWORD of the file size
sharedMemoryName_.c_str());
Ещё в проекте нашлись места использования функций, которые в 64-битной версии считаются устаревшими и должны быть заменены на соответствующие новые реализации. Например, GetWindowLong/SetWindowLong следует заменить на GetWindowLongPtr/SetWindowLongPtr.
PVS-Studio находит все приведённые примеры и другие виды 64-битных ошибок.
Роль статического анализатора PVS-Studio при 64-битной миграции
Часть потенциальных ошибок при миграции на 64-битную платформу находит компилятор и выдаёт соответствующие предупреждения. PVS-Studio лучше справляется с этой задачей, так как инструмент изначально разрабатывался с целью находить все такие ошибки. Более подробно о том, какие 64-битные ошибки находит PVS-Studio и не находят компилятор и статический анализатор Visual Studio, можно прочитать в статье "64-битный код в 2015 году: что нового в диагностике возможных проблем?".Хочется обратить внимание ещё на один важный момент. Регулярно используя статический анализатор, мы могли постоянно наблюдать, как исчезают старые, а иногда добавляются новые 64-битные ошибки. Ведь код постоянно правят десятки программистов. И иногда они могут ошибиться и внести 64-битную ошибку в проект, который уже адаптирован к x64. Если бы не статический анализ, было бы невозможно сказать, сколько ошибок исправлено, сколько внесено, и на каком этапе мы сейчас находимся. Благодаря PVS-Studio мы строили графики, которые помогали нам иметь представление о прогрессе. Но это уже тема для отдельной статьи.
Заключение
Для того, чтобы 64-битная миграция вашего проекта прошла как можно более спокойно, последовательность шагов должна быть следующей:- Изучить теорию (например, наши статьи).
- Найти все 64-битные библиотеки, которые используются в проекте.
- Максимально быстро собрать 64-битную версию, которая компилируется и линкуется.
- Исправить все 64-битные сообщения первого уровня анализатора PVS-Studio (64 L1).
Что почитать про 64-битную миграцию?
- Коллекция примеров 64-битных ошибок в реальных программах.
- Уроки разработки 64-битных приложений на языке Си/Си++.
- C++11 и 64-битные ошибки
- 64-битный код в 2015 году: что нового в диагностике возможных проблем?
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: How to Port a 9 Million Code Line Project to 64 bits?.
Комментарии (52)
danfe
03.08.2015 13:11+3Про 64-битную миграцию еще может быть полезно почитать эппловский 64-Bit Transition Guide.
Nikobraz
03.08.2015 13:21+1Вопрос может быть не совсем по теме:
Зачем переводят на 64 бита мелкие утилитки. типа WinMTR?
Какой профит от использования 64-битных версий? Они, ведь, даже в страшном кошмаре не должны жрать больше 4Гб памяти.
К большим проектам вопросов нет.evnuh
03.08.2015 13:35Вы же не считаете, что софт переписывают под 64, чтобы он смог больше памяти сожрать?)
EvgeniyRyzhkov
03.08.2015 13:39+2Системные утилиты часто просто необходимо сделать 64-битными, чтобы они были полноценными. Например, FAR.
Nikobraz
03.08.2015 13:48Почему? Вызовы системных функций проще? Или что еще?
EvgeniyRyzhkov
03.08.2015 13:52+10Из-за подсистемы WoW64. Если 32-битный FAR запустить, то для него эмулируются 64-битные папки. А для 64-битного FAR файловая структура будет такой, какой и должна быть.
Paul
06.08.2015 09:09+332-битный фар с незапамятных времен умеет заходить в неэмулируемый System32. А 32-битный плагин RegEdit под него — в 64-битный реестр.
a553
03.08.2015 14:00+3Ещё 32-битные процессы не умеют полноценно работать с 64-битными процессами через хендл (но не наоборот).
halyavin
03.08.2015 13:53Почему нельзя обойтись без ifdef'a?
typedef ptrdiff_t MyLong; typedef size_t MyULong;
EvgeniyRyzhkov
03.08.2015 13:58+2Потому что в 32-битном вариант MyLong является long. Это особенность конкретного проекта, которую изменить нельзя в силу большой кодовой базы.
halyavin
03.08.2015 15:13Они имеют одинаковый размер и знаковость. В чем разница?
Andrey2008
03.08.2015 15:21size_t — это unsigned int. Естественно unsigned long и unsigned int это разные типы.
Просто так взять и заменить нельзя. Когда проект такого размера, есть масса нюансов, которые вот так просто нельзя взять и победить. Одним словом, так было надо. :)
Gorthauer87
03.08.2015 15:40Кстати, интересно, а насколько давно все эти memsize типы появились?
Почему ими так неаккуратно пользуются, хотя си уже за 40 лет, а плюсам 30 с небольшим.Andrey2008
03.08.2015 15:56+1Как давно появились собственно и не важно. Дело в том, что буквально до последнего времени про это никто даже не думал. В книжках выпущенных всего несколько лет назад фигурирует грубо говоря for (int i =… ). И только заглядывая в книги последних лет, я начал видеть там size_t. Соответственно эти новые веяния программисты начинают брать на вооружение только сейчас. И дай бог лет через 5-10 будут чаще писать size_t, чем, например ulong. Пока до этого далеко.
Gorthauer87
03.08.2015 16:04Но ведь в api же наверняка правильные типы были. И разве при портировании с 16 бит на 32 разработчики не столкнулись с тем же ворохом проблем?
Andrey2008
03.08.2015 16:55+2Программист идеальный и программист реальный — это два мало похожих человека :). А по поводу миграции 16 на 32 бита, да, были схожие проблемы. НО!
- В проектах тех лет было на пару порядков меньше кода. И его можно было быстро просмотреть и поправить.
- 32-битынй Int вместил в себя указатель и максимальный размер массива, в отличие от нынешней ситуации.
В результате, портирование шло во много раз проще.Gorthauer87
03.08.2015 17:01То есть, нужные выводы не были сделаны потому, что всё само собой заработало?
EvgeniyRyzhkov
03.08.2015 17:06+1Скорее все-таки большинство софта переписали тогда просто. А сейчас переписывать было не резон.
mapron
04.08.2015 09:06Вроде, если не изменяет память под 16-битными int и был 16 бит. На 32 — стал 32, вот и не парились. А есть еще такая замечательная вещь как short long =)
beeruser
05.08.2015 01:58Эта проблема была только на PC/DOS.
Остальные платформы, основанные на 68k, MIPS, Sparc и т.д. 32-битные изначально.
Levhav
04.08.2015 02:36А вы могли бы описать общее качество кода? Сколько примерно из 9млн строк можно отнести к категории говнокода.
EvgeniyRyzhkov
04.08.2015 06:29+2Общее качество кода — обычный код. В котором видна история. Вот эти модули писались, когда вышла MFC. Вот эти — когда COM появился. Как кольца на стволе дерева.
P.S. Мне не очень нравится термин «говнокод». Если код приносит миллионы долларов, то называть его говнокодом язык не поворачивается.ZoomZoomZoom
04.08.2015 12:18Сомнительная логика. Многие программисты предпочитают рассматривать своё занятие если не как искусство, то как утончённое ремесло. В таких вещах качество не тождественно окупаемости.
erlyvideo
04.08.2015 13:29+1Только непонятно как это отношение относится к окупаемости и успешности продукта.
BalinTomsk
04.08.2015 03:01+1У меня был проект 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 не сработалиEvgeniyRyzhkov
04.08.2015 06:26+2Тесты были, и тестов было очень много. Без них вообще бы невозможно было.
darkfrei
04.08.2015 07:41А к чему сводится алгоритм обратного портирования, 64 на 32?
EvgeniyRyzhkov
04.08.2015 08:37+11. Создать машину времени.
2. Слетать в будущее, взять там 64-битный проект.
3. Вернуться в настоящее, просто запустить его на 32 битах.
А если серьезно, то во время портирования есть такой этап, когда компилируется и 32-битная, и 64-битная версия. Как правило это остается навсегда и так, чтобы 32-битной версии не было — это довольно редкая ситуация.Paul
06.08.2015 09:13Какой смысл в 2015 году продолжать поддерживать 32-битную версию приложения? Если ее выбросить, вдвое уменьшится время на сборку билдов и уйдет порядочная часть #ifdefов.
EvgeniyRyzhkov
06.08.2015 09:29Клиенты… Которые пользуются 32-битной версией по разным причинам. И силком их перетащить не всегда разумно с точки зрения бизнеса.
erlyvideo
04.08.2015 09:46Я не сталкивался с проблемами перехода 32 на 64 бита в своих маленьких кусках кода на C под линуксом.
Есть какая-то практическая разница между линуксом и виндовсом в плане этой миграции?EvgeniyRyzhkov
04.08.2015 10:03Есть разница при миграции маленьких кусков (которые можно за вечер просмотреть) и проекта на 300Mb.
erlyvideo
04.08.2015 13:29меня удивило количество адресной арифметики в таком большом проекте. Выглядит как какое-то бизнес-приложение, зачем в нём шаманить с указателями?
EvgeniyRyzhkov
04.08.2015 13:38-1Есть смысл во всем этом, не просто так они. (sorry за сухой ответ, NDA)
erlyvideo
04.08.2015 13:52+1К сожалению, ваша статья больше похожа на «мы с NDA делали NDA а там NDA потому что NDA».
Я понимаю, что вы сделали полезную штуку и заказчик наверное рад, но как статья вышло не очень. Даже с рекламной точки зрения для девелоперов вышло скучно. Возможно стоило детальнее рассказать про какой-то один кейс, который не под NDA.
Ну и, конечно, подобная мелочевка выглядит очень нелепо: под NDA закрывать даже то, почему нужна адресная арифметика. Может причины тому и есть, но всем вокруг на них откровенно наплевать.halyavin
04.08.2015 15:12Из того что раскрыто, видно, что в проекте используется обращение к WinAPI, в том числе прямая работа с оконными сообщениями. При обработке и посылке оконных сообщений часто приходится преобразовывать указатели в числа и обратно.
erlyvideo
04.08.2015 17:27тут я совершенно не в теме, поэтому это мне непонятно.
В API получается хитрят и просят положить пойнтер в поле, которое шириной в 4 байта?halyavin
04.08.2015 18:10+3https://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 битной системе «и так работает».
iSage
04.08.2015 10:23Wait, wait, т.е. вместо того, чтобы взять fixed-size типы из stdint вы наоборот, меняли все на архитектурно-зависимые? Но зачем?
EvgeniyRyzhkov
04.08.2015 10:34Указатель на одной платформе 4 байта, а на другой 8. И с этим ничего не поделаешь.
iSage
04.08.2015 10:40intptr_t?
EvgeniyRyzhkov
04.08.2015 10:43Ну в статье так и написано. Только ptrdiff_t выбрали, как более знакомый людям.
iSage
04.08.2015 10:55Нет, в статье у вас сплошные #if defined
Более того, ptrdiff_t, внезапно, может отличаться от intptr_t (хоть в данном случае и не), не стоит использовать его просто потому, что «более знаком». Он нужен именно для хранения разницы указателей. Более того, внутри одного массива/структуры.Andrey2008
04.08.2015 11:40+1Статья — это микроскопические щели, через которые видны отдельные элементы слона, такие как соломинка, прилипшая к туловищу, один глаз, и испачканный бивень :). Не стоит по этим элементам судить о слоне целиком. То, что в статью попали #ifdef и объявления типов, ничего не значит и не стоит их обсуждать. Если бы в статье было написано про тип time_t и проблемы с ним связанные, то, казалось бы, что в основном мы работали именно с этим. Это не так. Если бы написали про правки дистрибутива, казалось, бы что именно это сложно и важно. Это не так.
Над проектом миграции полтора года работало несколько человек. Был проделан большой объем разнообразных работ. Слишком много всего, чтобы поместить всё в одну статью. И отдельные моменты, описанные здесь могут казаться странными. Например, зачем мы делали такие странные типы данных. Но надо понимать, что это сложный компромисс, который очень долго обдумывался. Вопрос решался не только с точки зрения идеального мира, но и того, как оставить проект работоспособным и минимально мешать работе других.
По поводу #ifdef спешу успокоить. Мы максимально старательно их избегали. Но не всегда это возможно. Просто в статью попало упоминание #ifdef. Могло бы попасть что-то ещё и казаться важным.
Idot
04.08.2015 19:43А про legacy 16 и 8-бит код напишете? *серьёзно, не шучу* Часто такой код имеет вставки Ассемблера.
Darka
Неужели с помощью sed и awk?
EvgeniyRyzhkov
Забыл хаб «Блог компании sed и awk» добавить, а больше не редактируется :-(