Conditional compilation/ru

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) suomi (fi) français (fr) русский (ru)

Что такое условная компиляция?

Условная компиляция - это компиляция или пропуск части исходного кода в зависимости от того, существует условие или нет.

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

Они могут использоваться для различных целей, таких как:

  • изоляция кода для конкретной платформы
  • выбор естественного языка
  • лицензирование частей с открытым и закрытым исходным кодом
  • изоляция экспериментального кода
  • версия компилятора
  • версия библиотеки
  • и т.д. и т.п.

Free Pascal поддерживает четыре различных стиля условной компиляции:

  • Turbo Pascal и ранние директивы стиля Delphi
  • директивы стиля Mac Pascal
  • Современные директивы в стиле Free Pascal и Delphi
  • Макросы времени компиляции
Note-icon.png

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

Директивы в стиле Turbo Pascal

Директивами в стиле Turbo Pascal являются {$DEFINE}, {$IFDEF}, {$ENDIF}, {$IFNDEF}, {$IFOPT}, {$ELSE}, {$ELSEIF} и {$UNDEF}.

Мы опишем директивы в контексте стиля. Некоторые определения имеют расширенное значение в другом стиле.

Это означает, что в дальнейшем мы можем расширить значение некоторых директив, таких как, например, {$DEFINE}, в контексте макросов.

$define

Директива {$DEFINE} просто объявляет символ, который мы позже можем использовать для условной компиляции:

{$DEFINE name} //Это объявляет символ под названием "name"

Обратите внимание, что вы также можете объявить символ из командной строки или , например, -dDEBUG IDE, который будет являться эквивалентом командной строки

{$DEFINE DEBUG}

в исходном коде.

$undef

Директива {$UNDEF} отменяет объявление ранее определенного символа. Вот пример, который автор использует на практике:

// Некоторый старый исходный код, "загрязненый" обилием {$IFDEF FPC}, который больше 
// не нуждается в зависимости от версии Delphi, с которой он должен быть совместим. 
// Я всегда проверяю это, пробуя это поверх программы или модуля:
{$IFDEF FPC}
  {$MODE DELPHI}
  {$UNDEF FPC}
  {$DEFINE VER150} 
 // код теперь будет компилироваться так, как если бы это был Delphi 7, при условии, что исходный код Delphi действительно был написан для Delphi 7 и выше.
{$ENDIF}

$ifdef и $endif

Самый простой способ объявить блок условного кода так:

unit cross;
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}

Приведенный выше пример довольно распространен для исходного кода, который должен компилироваться как в Delphi, так и в Free Pascal.

Если компилятором является Delphi, то ничего не делается, но если компилятором является Free Pascal, он переключит Free Pascal для компиляции и использования режима синтаксиса Delphi.

Этот условный символ "FPC" объявлен в системе - там их длинный список. Синтаксис блоков {$IFDEF} и {$ENDIF} является симметричным: каждый {$IFDEF} имеет свой собственный {$ENDIF}.

Чтобы помочь вам распознать соответствующие блоки, вы можете использовать, например, отступ, но вы также можете использовать функцию комментариев:

{$IFDEF FPC эта часть специфична для Free Pascal}
// некий Free Pascal специфичный код
{$ENDIF Free Pascal специфичный код}
Warning-icon.png

Предупреждение: Эта особенность комментариев часто не совсем понятна. Некоторые люди - как и в более старой версии этой записи вики - предположили, что вы можете вкладывать {$IFDEF}, потому что компилятор, кажется, принимает синтаксис. Но первое неверно, а второе верно: да, компилятор принимает приведенный ниже синтаксис, но это не вложенное {$IFDEF}, а одиночное условие {$IFDEF}, а остальное - комментарий! Приведенный ниже код выполнит writeln тогда и только тогда, когда будет объявлен {$define red}. В этом примере {$ifdef blue} является комментарием! Даже если {$define blue} допустим.

// программа формально будет скомпилирована, но полностью неработоспособна
{$define blue}  
begin
{$ifdef red or $ifdef blue}// все после red - это комментарий 
  writeln ('red or blue'); // этот код никогда не выполнится
{$endif red or blue}       // все, что объявлено после $endif, является комментарием.
end.

$ifndef

Это противоположно {$IFDEF}, и ниже лежащий код выполнится, если объявленное условие не выполняется. Простой пример:

{$IFNDEF FPC эта часть не для Free Pascal}
// некий конкретный код, который Free Pascal не должен компилировать
{$ENDIF код для других компиляторов, кроме Free Pascal}

$else and $elseif

{$ELSE} is used to compile code that does not belong to the code block that is defined by the corresponding {$IFDEF}. It is also valid in the context {$IFOPT}, {$IF} or {$IFC} that we will discuss later.

{$IFDEF red}  
   writeln('Red is defined');  
{$ELSE  no red}  
  {$IFDEF blue}  
   writeln('Blue is defined, but red is not defined');  
  {$ELSE no blue}  
  writeln('Neither red nor blue is defined'); 
  {$ENDIF blue}  
{$ENDIF red}

Such nested conditional written in the above syntax can get very messy and unreadable. Luckily we can simplify it a lot by using {$ELSEIF}. The code below is an expanded equivalent of the first example:

{$IF Defined(red)}  
  writeln('Red is defined');  
{$ELSEIF Defined(blue)}  
  writeln('Blue is defined');  
{$ELSEIF Defined(green)}  
  writeln('Green is defined');   
{$ELSE}
  writeln('Neither red, blue or green. Must be black...or something else...');
{$ENDIF}

As you can see this is a lot more readable.

$ifopt

С помощью {$IFOPT} мы можем проверить, задана ли определенная опция компиляции.

Из руководства по программированию:

 {$IFOPT switch} скомпилирует текст, который следует за ним, если переключатель switch 
 в данный момент будет находиться в указанном состоянии. Если он не находится в указанном состоянии, 
 то компиляция продолжится после соответствующей директивы {$ELSE} или {$ENDIF}.

Например:

 {$IFOPT M+}  
   Writeln('Compiled with type information');  
 {$ENDIF}

Скомпилирует оператор Writeln, только если включена генерация информации о типе.

Note-icon.png

Примечание: директива {$IFOPT} принимает только короткие опции, т.е. {$IFOPT TYPEINFO} не будет принята.

Обычно этот пример используется для проверки, задан ли режим DEBUG:

{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}

Такие определения также могут находиться в конфигурационных файлах, таких как fpc.cfg, которые также содержат полное объяснение того, как использовать:

# ----------------------
# Defines (preprocessor)
# ----------------------
#
# nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed
#
# -d is the same as #DEFINE
# -u is the same as #UNDEF
#
#
# Some examples (for switches see below, and the -? help pages)
#
# Try compiling with the -dRELEASE or -dDEBUG on the command line
#
# For a release compile with optimizes and strip debug info
#IFDEF RELEASE
  -O2
  -Xs
  #WRITE Compiling Release Version
#ENDIF

What not to do

What is wrong with this code? Can you spot it?

var
  MyFilesize:
  {$ifdef Win32} 
    Cardinal 
  {$else}
    int64
  {$endif}

The answer is:

  • that Free Pascal compiles for more CPU types than 32 an 64 bit, also for e.g. 8 and 16 bit.
  • on most 64 bit platforms the maximum file size is a QWord, not an Int64.

That programmer fell into a trap that is common: if you use a define, make sure your logic is solid. Otherwise such code can easily cause accidents. The compiler will not catch your logic errors!

It is always good to realize such things especially that such things can easily be fixed.

var
  MyFilesize:
  {$if defined(Win32)} 
    Cardinal 
  {$elseif defined(Win64)}
    Qword;
  {$else}
     {$error this code is written for win32 or win64}
  {$endif}

As an aside of course there is a solution for this particular example that does not use conditionals at all:

var
  MyFilesize:NativeUint;

What is wrong with this code? Can you spot it?

  
{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF}
{$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}

The Answer is:

  • Well, I have already wrote a comment that warned you.. so look at the warning.... You should be able to spot it...
  • Compiler directives override the compiler... be careful with that axe Eugene.
Directives, definitions and conditionals definitions
global compiler directives • local compiler directives

Conditional Compiler Options • Conditional compilation • Macros and Conditionals • Platform defines
$IF