64 бита? О, интересненько!
Недавно наша команда завершила миграцию на 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-битной миграции нужна, естественно, замена некоторых типов данных на 64-битные. Если в проекте такого размера создать отдельную ветку и внести там все необходимые правки, то объединить код (выполнить merge) будет невозможно! Не забывайте об объеме проекта и нескольких десятках программистов, которые ежедневно пишут новый код.

В силу бизнес-ограничений заказчик не мог остановить процесс разработки. Его клиентам постоянно нужны новые релизы, исправления проблем, специальные возможности и т.п. Остановить разработку в таких условиях означает остановить бизнес. Поэтому заказчик стал искать команду, которая может выполнить миграцию без остановки процесса разработки. Такой командой стали мы, так как наша компетенция в 64-битной разработке подтверждается анализатором кода PVS-Studio и статьями по данной тематике.

Мы выполнили миграцию за полтора года. С нашей стороны в проекте первые полгода принимали участие два человека, затем еще год уже четыре человека. Почему так долго? Первые полгода два человека занимались настройкой инфраструктуры, знакомством с проектом, проверкой конкретных способов миграции. Затем, через полгода, когда задача стала уже более конкретной, к проекту подключились еще люди и уже 4 человека за год выполнили миграцию.

Как перенести проект на 64-битную систему?

Перенос проекта на 64-битную платформу по большому счёту заключается в следующих двух шагах:
  1. Создание 64-битной конфигурации, получение 64-битных версий сторонних библиотек и сборка проекта.
  2. Исправление кода, который приводит к ошибкам в 64-битной версии. Этот пункт почти полностью сводится к тому, что нужно заменить 32-битные типы на memsize-типы в коде программы.
Напомним, что под memsize-типами понимают типы переменной размерности. Такие типы имеют размер 4 байта на 32-битной системе и 8 байт на 64-битной.

Портирование большого и активно развивающегося проекта не должно мешать текущей разработке, поэтому мы предприняли следующие меры. Во-первых, мы делали все наши правки в отдельной ветке, чтобы не ломать основную сборку. Когда очередной набор наших изменений был готов и протестирован, мы объединяли наши изменения с основной веткой. А во-вторых, мы не стали менять жёстко 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. Хотя такой способ работает для небольших проектов, в нашем случае он не подошёл. Во-первых, замена типов занимала слишком много времени и приводила к большому количеству изменений. А во-вторых, как мы ни старались делать это аккуратно, мы, тем не менее, правили по ошибке структуры с форматами данных. В результате, когда мы закончили работать над первой частью проектов и запустили приложение, мы не смогли загрузить предустановленные шаблоны интерфейсов, так как они были бинарными.

Итак, первый план предполагал следующую последовательность действий.
  1. Создание 64-битной конфигурации.
  2. Компиляция.
  3. Замена большинства 32-битных типов на 64-битные (вернее на memsize-типы).
  4. Линковка со сторонними библиотеками.
  5. Запуск приложения.
  6. Правка оставшихся предупреждений компилятора.
  7. Правка оставшихся 64-битных ошибок, которые выявит анализатор PVS-Studio.
И этот план был признан неудачным. Мы выполнили первые пять пунктов, и все наши изменения в исходном коде пришлось откатить. Мы потратили впустую несколько месяцев работы.

Теперь мы решили сначала как можно быстрее получить рабочую 64-битную версию приложения, а потом уже поправить явные 64-битные ошибки. Наш план теперь исключал массовую замену типов и предполагал правку только явных 64-битных ошибок:
  1. Создание 64-битной конфигурации.
  2. Компиляция.
  3. Линковка со сторонними библиотеками.
  4. Запуск приложения.
  5. Правка предупреждений компилятора.
  6. Правка самых приоритетных 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-битная миграция вашего проекта прошла как можно более спокойно, последовательность шагов должна быть следующей:
  1. Изучить теорию (например, наши статьи).
  2. Найти все 64-битные библиотеки, которые используются в проекте.
  3. Максимально быстро собрать 64-битную версию, которая компилируется и линкуется.
  4. Исправить все 64-битные сообщения первого уровня анализатора PVS-Studio (64 L1).

Что почитать про 64-битную миграцию?


  1. Коллекция примеров 64-битных ошибок в реальных программах.
  2. Уроки разработки 64-битных приложений на языке Си/Си++.
  3. C++11 и 64-битные ошибки
  4. 64-битный код в 2015 году: что нового в диагностике возможных проблем?



Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: How to Port a 9 Million Code Line Project to 64 bits?.

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


  1. Darka
    03.08.2015 12:30
    +6

    Неужели с помощью sed и awk?


    1. EvgeniyRyzhkov
      03.08.2015 12:38
      +16

      Забыл хаб «Блог компании sed и awk» добавить, а больше не редактируется :-(


  1. danfe
    03.08.2015 13:11
    +3

    Про 64-битную миграцию еще может быть полезно почитать эппловский 64-Bit Transition Guide.


  1. Nikobraz
    03.08.2015 13:21
    +1

    Вопрос может быть не совсем по теме:
    Зачем переводят на 64 бита мелкие утилитки. типа WinMTR?
    Какой профит от использования 64-битных версий? Они, ведь, даже в страшном кошмаре не должны жрать больше 4Гб памяти.
    К большим проектам вопросов нет.


    1. evnuh
      03.08.2015 13:35

      Вы же не считаете, что софт переписывают под 64, чтобы он смог больше памяти сожрать?)


    1. EvgeniyRyzhkov
      03.08.2015 13:39
      +2

      Системные утилиты часто просто необходимо сделать 64-битными, чтобы они были полноценными. Например, FAR.


      1. Nikobraz
        03.08.2015 13:48

        Почему? Вызовы системных функций проще? Или что еще?


        1. EvgeniyRyzhkov
          03.08.2015 13:52
          +10

          Из-за подсистемы WoW64. Если 32-битный FAR запустить, то для него эмулируются 64-битные папки. А для 64-битного FAR файловая структура будет такой, какой и должна быть.


          1. EvilFox
            05.08.2015 19:34
            +2

            Передаём Wow64EnableWow64FsRedirection False и такой проблемы нет.


          1. Paul
            06.08.2015 09:09
            +3

            32-битный фар с незапамятных времен умеет заходить в неэмулируемый System32. А 32-битный плагин RegEdit под него — в 64-битный реестр.


        1. a553
          03.08.2015 14:00
          +3

          Ещё 32-битные процессы не умеют полноценно работать с 64-битными процессами через хендл (но не наоборот).


  1. halyavin
    03.08.2015 13:53

    Почему нельзя обойтись без ifdef'a?

    typedef ptrdiff_t MyLong;
    typedef size_t MyULong;
    


    1. EvgeniyRyzhkov
      03.08.2015 13:58
      +2

      Потому что в 32-битном вариант MyLong является long. Это особенность конкретного проекта, которую изменить нельзя в силу большой кодовой базы.


      1. halyavin
        03.08.2015 15:13

        Они имеют одинаковый размер и знаковость. В чем разница?


        1. Andrey2008
          03.08.2015 15:21

          size_t — это unsigned int. Естественно unsigned long и unsigned int это разные типы.
          Просто так взять и заменить нельзя. Когда проект такого размера, есть масса нюансов, которые вот так просто нельзя взять и победить. Одним словом, так было надо. :)


  1. Gorthauer87
    03.08.2015 15:40

    Кстати, интересно, а насколько давно все эти memsize типы появились?
    Почему ими так неаккуратно пользуются, хотя си уже за 40 лет, а плюсам 30 с небольшим.


    1. Andrey2008
      03.08.2015 15:56
      +1

      Как давно появились собственно и не важно. Дело в том, что буквально до последнего времени про это никто даже не думал. В книжках выпущенных всего несколько лет назад фигурирует грубо говоря for (int i =… ). И только заглядывая в книги последних лет, я начал видеть там size_t. Соответственно эти новые веяния программисты начинают брать на вооружение только сейчас. И дай бог лет через 5-10 будут чаще писать size_t, чем, например ulong. Пока до этого далеко.


      1. Gorthauer87
        03.08.2015 16:04

        Но ведь в api же наверняка правильные типы были. И разве при портировании с 16 бит на 32 разработчики не столкнулись с тем же ворохом проблем?


        1. Andrey2008
          03.08.2015 16:55
          +2

          Программист идеальный и программист реальный — это два мало похожих человека :). А по поводу миграции 16 на 32 бита, да, были схожие проблемы. НО!

          • В проектах тех лет было на пару порядков меньше кода. И его можно было быстро просмотреть и поправить.
          • 32-битынй Int вместил в себя указатель и максимальный размер массива, в отличие от нынешней ситуации.

          В результате, портирование шло во много раз проще.


          1. Gorthauer87
            03.08.2015 17:01

            То есть, нужные выводы не были сделаны потому, что всё само собой заработало?


            1. EvgeniyRyzhkov
              03.08.2015 17:06
              +1

              Скорее все-таки большинство софта переписали тогда просто. А сейчас переписывать было не резон.


          1. mapron
            04.08.2015 09:06

            Вроде, если не изменяет память под 16-битными int и был 16 бит. На 32 — стал 32, вот и не парились. А есть еще такая замечательная вещь как short long =)


        1. beeruser
          05.08.2015 01:58

          Эта проблема была только на PC/DOS.
          Остальные платформы, основанные на 68k, MIPS, Sparc и т.д. 32-битные изначально.


  1. Levhav
    04.08.2015 02:36

    А вы могли бы описать общее качество кода? Сколько примерно из 9млн строк можно отнести к категории говнокода.


    1. gaelpa
      04.08.2015 04:26
      +2

      На месте заказчика я бы внес запрет отвечать на подобные вопросы в NDA )


    1. EvgeniyRyzhkov
      04.08.2015 06:29
      +2

      Общее качество кода — обычный код. В котором видна история. Вот эти модули писались, когда вышла MFC. Вот эти — когда COM появился. Как кольца на стволе дерева.

      P.S. Мне не очень нравится термин «говнокод». Если код приносит миллионы долларов, то называть его говнокодом язык не поворачивается.


      1. ZoomZoomZoom
        04.08.2015 12:18

        Сомнительная логика. Многие программисты предпочитают рассматривать своё занятие если не как искусство, то как утончённое ремесло. В таких вещах качество не тождественно окупаемости.


        1. erlyvideo
          04.08.2015 13:29
          +1

          Только непонятно как это отношение относится к окупаемости и успешности продукта.


  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 не сработали


    1. EvgeniyRyzhkov
      04.08.2015 06:26
      +2

      Тесты были, и тестов было очень много. Без них вообще бы невозможно было.


  1. darkfrei
    04.08.2015 07:41

    А к чему сводится алгоритм обратного портирования, 64 на 32?


    1. EvgeniyRyzhkov
      04.08.2015 08:37
      +1

      1. Создать машину времени.
      2. Слетать в будущее, взять там 64-битный проект.
      3. Вернуться в настоящее, просто запустить его на 32 битах.

      А если серьезно, то во время портирования есть такой этап, когда компилируется и 32-битная, и 64-битная версия. Как правило это остается навсегда и так, чтобы 32-битной версии не было — это довольно редкая ситуация.


      1. Paul
        06.08.2015 09:13

        Какой смысл в 2015 году продолжать поддерживать 32-битную версию приложения? Если ее выбросить, вдвое уменьшится время на сборку билдов и уйдет порядочная часть #ifdefов.


        1. EvgeniyRyzhkov
          06.08.2015 09:29

          Клиенты… Которые пользуются 32-битной версией по разным причинам. И силком их перетащить не всегда разумно с точки зрения бизнеса.


  1. erlyvideo
    04.08.2015 09:46

    Я не сталкивался с проблемами перехода 32 на 64 бита в своих маленьких кусках кода на C под линуксом.

    Есть какая-то практическая разница между линуксом и виндовсом в плане этой миграции?


    1. EvgeniyRyzhkov
      04.08.2015 10:03

      Есть разница при миграции маленьких кусков (которые можно за вечер просмотреть) и проекта на 300Mb.


      1. erlyvideo
        04.08.2015 13:29

        меня удивило количество адресной арифметики в таком большом проекте. Выглядит как какое-то бизнес-приложение, зачем в нём шаманить с указателями?


        1. EvgeniyRyzhkov
          04.08.2015 13:38
          -1

          Есть смысл во всем этом, не просто так они. (sorry за сухой ответ, NDA)


          1. erlyvideo
            04.08.2015 13:52
            +1

            К сожалению, ваша статья больше похожа на «мы с NDA делали NDA а там NDA потому что NDA».

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

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


            1. halyavin
              04.08.2015 15:12

              Из того что раскрыто, видно, что в проекте используется обращение к WinAPI, в том числе прямая работа с оконными сообщениями. При обработке и посылке оконных сообщений часто приходится преобразовывать указатели в числа и обратно.


              1. erlyvideo
                04.08.2015 17:27

                тут я совершенно не в теме, поэтому это мне непонятно.

                В API получается хитрят и просят положить пойнтер в поле, которое шириной в 4 байта?


                1. halyavin
                  04.08.2015 18:10
                  +3

                  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 битной системе «и так работает».


                  1. erlyvideo
                    04.08.2015 19:42

                    т.е. проблема в том, что API дизайнилось с явными ошибками?


                    1. halyavin
                      04.08.2015 20:10

                      Явных ошибок нет, просто нужна аккуратность в его использовании.


                      1. erlyvideo
                        04.08.2015 20:13

                        аа, т.е. раньше работало хоть и с варнингами то, что работать не должно было?


  1. iSage
    04.08.2015 10:23

    Wait, wait, т.е. вместо того, чтобы взять fixed-size типы из stdint вы наоборот, меняли все на архитектурно-зависимые? Но зачем?


    1. EvgeniyRyzhkov
      04.08.2015 10:34

      Указатель на одной платформе 4 байта, а на другой 8. И с этим ничего не поделаешь.


      1. iSage
        04.08.2015 10:40

        intptr_t?


        1. EvgeniyRyzhkov
          04.08.2015 10:43

          Ну в статье так и написано. Только ptrdiff_t выбрали, как более знакомый людям.


          1. iSage
            04.08.2015 10:55

            Нет, в статье у вас сплошные #if defined
            Более того, ptrdiff_t, внезапно, может отличаться от intptr_t (хоть в данном случае и не), не стоит использовать его просто потому, что «более знаком». Он нужен именно для хранения разницы указателей. Более того, внутри одного массива/структуры.


            1. Andrey2008
              04.08.2015 11:40
              +1

              Статья — это микроскопические щели, через которые видны отдельные элементы слона, такие как соломинка, прилипшая к туловищу, один глаз, и испачканный бивень :). Не стоит по этим элементам судить о слоне целиком. То, что в статью попали #ifdef и объявления типов, ничего не значит и не стоит их обсуждать. Если бы в статье было написано про тип time_t и проблемы с ним связанные, то, казалось бы, что в основном мы работали именно с этим. Это не так. Если бы написали про правки дистрибутива, казалось, бы что именно это сложно и важно. Это не так.

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

              По поводу #ifdef спешу успокоить. Мы максимально старательно их избегали. Но не всегда это возможно. Просто в статью попало упоминание #ifdef. Могло бы попасть что-то ещё и казаться важным.


  1. Idot
    04.08.2015 19:43

    А про legacy 16 и 8-бит код напишете? *серьёзно, не шучу* Часто такой код имеет вставки Ассемблера.