Как подготовиться к 64-битам при переходе на Delphi 2010 и Unicode

Поскольку в следующей версии не ожидается поддержка 64 бит, больше не будет возможности дождаться возможности перенести нашу существующую базу кода в unicode и 64-bit за один раз. Однако было бы неплохо, если бы мы уже подготовили наш код для 64-бит при выполнении перевода в Юникод. Это позволит свести к минимуму воздействие в случае, если оно, наконец, появится в версии 2020. Любые предложения, как подойти к этому, не введя много шума, если он не появится до 2020 года?

Есть еще один подобный вопрос , но я тоже повторю свой ответ, чтобы убедиться, что многие люди видят эту информацию:

Прежде всего, отказ от ответственности: хотя я работаю на Embarcadero. Я не могу говорить за своего работодателя. То, что я собираюсь написать, основано на моем собственном мнении о том, как должна работать гипотетическая 64-разрядная Delphi, но могут быть или не быть конкурирующие мнения и другие предусмотренные или непредвиденные несовместимости и события, которые вызывают альтернативные дизайнерские решения.

Это сказало:

  • Существует два целочисленных типа: NativeInt и NativeUInt, размер которых будет плавать между 32-битными и 64-битными в зависимости от платформы. Они были вокруг для нескольких выпусков. Никакие другие целочисленные типы не изменят размер в зависимости от битности цели.

  • Убедитесь, что любое место, которое полагается на листинг значения указателя на целое число или наоборот, использует NativeInt или NativeUInt для целочисленного типа. TComponent.Tag должен быть NativeInt в более поздних версиях Delphi.

  • Я бы предложил не использовать NativeInt или NativeUInt для значений, не содержащих указателей . Постарайтесь, чтобы ваш код семантически совпал между 32-битным и 64-битным. Если вам нужно 32 бита диапазона, используйте Integer; если вам нужны 64 бита, используйте Int64. Таким образом, ваш код должен работать одинаково на обеих битах. Только если вы используете значение «Указатель» или какое-либо значение, например, ссылку или THandle, следует использовать NativeInt.

  • Указательные вещи должны следовать аналогичным правилам указателям: ссылки на объекты (очевидно), но также такие вещи, как HWND, THandle и т. Д.

  • Не полагайтесь на внутренние детали строк и динамических массивов, например, их данные заголовка.

  • Наша общая политика по изменениям API для 64-битной версии должна заключаться в том, чтобы поддерживать один и тот же API между 32-битными и 64-битными, если это возможно, даже если это означает, что 64-битный API не обязательно использует преимущество машины. Например, TList, вероятно, будет обрабатывать только элементы MaxInt divOpOp (Pointer), чтобы сохранить Count, индексы и т. Д. Как Integer. Поскольку тип Integer не будет плавать (т. Е. Изменять размер в зависимости от битности), мы не хотим иметь волновой эффект на код клиента: любые индексы, округленные с помощью переменной Integer или индексом for-loop, будут быть усеченным и потенциально вызывать тонкие ошибки.

  • В тех случаях, когда API-интерфейсы расширены для 64-разрядных, они, скорее всего, будут выполнены с дополнительным свойством function / method / для доступа к дополнительным данным, и этот API также будет поддерживаться в 32-разрядной версии. Например, стандартная процедура Length (), вероятно, вернет значения типа Integer для аргументов строки типа или динамического массива; если вы хотите иметь дело с очень большими динамическими массивами, может быть и процедура LongLength (), реализация которой в 32-битной форме совпадает с Length (). Length () генерирует исключение в 64-битном случае, если применяется к динамическому массиву с более чем 232 элементами.

  • В связи с этим, вероятно, будет улучшена проверка ошибок для сужения операций на языке, особенно сужение 64-битных значений до 32-битных местоположений. Это повлияло бы на удобство назначения возвращаемого значения Length в местоположения типа Integer, если Length (), возвращенный Int64. С другой стороны, специально для функций-компиляторов, таких как Length (), может быть некоторое преимущество магии, например, для переключения типа возврата на основе контекста. Но преимущество не может быть реализовано в не магических API.

  • Динамические массивы, вероятно, поддерживают 64-битную индексацию. Обратите внимание, что массивы Java ограничены 32-разрядной индексацией даже на 64-битных платформах.

  • Строки, вероятно, будут ограничены 32-разрядной индексацией. Нам трудно найти реалистичные причины для людей, которым нужны строки размером 4 ГБ +, которые действительно являются строками, а не просто управляемые капли данных, для которых динамические массивы могут служить так же хорошо.

  • Возможно, встроенный ассемблер, но с ограничениями, например, неспособный свободно смешивать с кодом Delphi; существуют также правила вокруг исключений и макета фрейма стека, которые должны соблюдаться на x64.

Во-первых, посмотрите места, где вы взаимодействуете с не-delphi-библиотеками и api-вызовами, они могут отличаться. В Win32 библиотеки со стандартным вызовом stdcall называются _SomeFunction @ 4 (@ 4, указывающие размер параметров и т. Д.). На Win64 существует только одно соглашение о вызове, а функции в dll больше не оформлены. Если вы импортируете функции из DLL-файлов, вам может потребоваться их настройка.

Имейте в виду, что в 64-разрядной версии EXE вы не можете загрузить 32-разрядную DLL, поэтому, если вы зависите от сторонних DLL-файлов, вы должны проверить и 64-разрядную версию этих файлов.

Кроме того, посмотрите на Integer, если вы зависите от их максимального значения, например, когда вы позволяете им переполняться и ждать момента, когда это произойдет, это вызовет проблему, если будет изменен размер целого.

Кроме того, при работе с streamами, и вы хотите сериализовать разные данные, включая целое число, это вызовет проблему, так как размер целого изменится, и ваш stream будет не синхронизирован.

Таким образом, в местах, где вы зависите от размера целого или указателя, вам нужно будет внести коррективы. При сериализации данных sush вам также нужно иметь в виду эту проблему размера, поскольку это может привести к несовместимости данных между 32 и 64-битными версиями.

Кроме того, компилятор FreePascal с IDE Lazarus уже поддерживает 64-разрядную версию. Этот альтернативный компилятор Object Pascal не на 100% совместим с диалектом Borland / Codegear / Embarcadero Pascal, поэтому просто перекомпилировать его для 64-разрядного может быть не так просто, но это может помочь указать проблемы с 64-битным.

Преобразование в 64 бит не должно быть очень болезненным. Начните с преднамеренного размера целого числа, где это имеет значение. Не используйте «integer» вместо Int32 для целых чисел размером 32 бита, а Int64 для целых чисел размером 64 бит. В последнем преобразовании бит определение Integer перешло от Int16 к Int32, поэтому вы играете в нем безопасно, указав точную глубину бит.

Если у вас есть встроенная assembly, создайте эквивалент pascal и создайте некоторые модульные тесты, чтобы гарантировать, что они работают одинаково. Выполните некоторые временные тесты и убедитесь, что assembly все еще работает достаточно быстро, чтобы сохранить. Если да, то вы захотите внести изменения в оба, поскольку они необходимы.

Используйте NativeInt для целых чисел, которые могут содержать литые указатели.

Давайте будем гением компьютера.