Diretivas e compilação condicional
{$if}, {$define}, {$include}, {$macro}, {$message}, {$config} e as dicas de otimização.
Uma diretiva tem a forma {$nome …} (ou (*$nome …*)). O compilador a reconhece antes do comentário de chaves: {$ abre diretiva, { abre comentário.
Compilação condicional#
{$define TRACE}
{$ifdef TRACE}
WriteLn('depurando');
{$else}
...
{$endif}| Diretiva | Efeito |
|---|---|
{$define X} / {$undef X} | liga e desliga um símbolo |
{$ifdef X} / {$ifndef X} | texto condicional por símbolo |
{$if expr} … {$elseif expr} … {$else} … {$endif} | condicional por expressão (veja abaixo) |
{$include arquivo} | traz o conteúdo de um .fxi |
{$message hint|warning|error|fatal 'texto'} | mensagem do compilador (veja abaixo) |
Os símbolos da plataforma (MSWin64, MSWin…) são definidos pelo compilador; -D<símbolo> na linha de comando e Define na configuração definem outros.
{$if} e as funções de tipo#
{$if} aceita expressões sobre tipos e símbolos (Is(T, X), SizeOf(T), IsManaged(T), Defined(X), Declared(X)…). Dentro de um genérico ele é reavaliado na instanciação, com o T já trocado pelo tipo real.
program Conditional;
{$define TRACE}
type
TInfo<T> = class(TObject)
public
class function Describe: UString;
end class;
// dentro de um genérico, o {$if} é reavaliado na instanciação, com o T já trocado
class function TInfo<T>.Describe: UString;
begin
{$if Is(T, UInt8)}
Exit('um byte');
{$elseif IsString(T)}
Exit('um texto');
{$elseif SizeOf(T) < 8}
Exit('pequeno (' + IntToUStr(SizeOf(T)) + ' bytes)');
{$else}
Exit('grande (' + IntToUStr(SizeOf(T)) + ' bytes)');
{$endif}
end function;
begin
WriteLn(TInfo<UInt8>.Describe);
WriteLn(TInfo<UString>.Describe);
WriteLn(TInfo<Int32>.Describe);
WriteLn(TInfo<Int128>.Describe);
{$ifdef TRACE}
WriteLn('TRACE está ligado');
{$endif}
{$ifdef MSWin64}
WriteLn('compilado para Windows 64 bits');
{$endif}
end.um byte
um texto
pequeno (4 bytes)
grande (16 bytes)
TRACE está ligado
compilado para Windows 64 bits{$message}#
| Forma | O que faz |
|---|---|
{$message hint 'texto'} | sai na compilação; não muda o resultado |
{$message warning 'texto'} | o mesmo, com outro rótulo |
{$message error 'texto'} | entra na lista de erros; a leitura segue, e no fim ela mostra todos |
{$message fatal 'texto'} | entra na lista e para ali |
Sem uma severidade que se reconheça, o texto inteiro é a mensagem e ela vale como hint.
Macros#
{$macro Nome(P1, P2) corpo} guarda texto. Na invocação, %P1% é substituído e o resultado é lido pela mesma pilha do {$include}, então uma macro pode invocar outra, e um erro dentro da expansão aponta <macro Nome>(linha,coluna).
program Macros;
// {$macro} guarda TEXTO: na invocação, %Parâmetro% é substituído e o resultado
// é lido como se estivesse escrito ali.
{$macro MaxFunc(Type, Name)
function %Name%(const A, B: %Type%): %Type%;
begin
if A > B then
Exit(A);
Exit(B);
end;}
MaxFunc('Int32', 'MaxInt');
MaxFunc('Float64', 'MaxFloat');
begin
WriteLn(MaxInt(3, 9));
WriteLn(MaxFloat(2.5, 1.5));
end.9
2.5%%é um%literal.- Uma macro pode expandir para comandos, não só para declarações.
- O corpo pode ter chaves
{ }dentro; o comentário não termina a macro antes da hora. {$undefmacro Nome}devolve o nome, e depois ele aceita outra definição.- Uma macro sem parâmetros é
{$macro Nome() …}.
As sobrecargas de Min e Max da RTL são geradas assim: vinte cópias das mesmas linhas, mudando só o tipo.
{$config}#
{$config Chave valor} ajusta o compilador. O alcance (local, global ou region) diz até onde vale: este módulo (omissão), a compilação toda, ou até o end; da região em vigor.
| Chave | O que faz |
|---|---|
AppType GUI|CONSOLE | subsistema do executável (padrão CONSOLE; GUI não abre janela de console) |
Manifest 'arquivo' · Icon 'arquivo' | liga o manifesto e o ícone ao executável |
Resource 'arquivo' | embute um arquivo como recurso (RCData) |
VerInfo … | informações de versão do executável |
WCLStyle 'arquivo' | embute um estilo visual (.vsf) da WCL |
platform MSWin64|Linux64 | alvo e símbolos da plataforma (e o triple do IR) |
inside CRT · outside CRT | a CRT dentro do binário (sempre roda) ou fora (binário menor) |
generate_llvm on|off | gravar o .ll e usar o clang, ou IR em memória (omissão) |
rangechecks on|off | verificação de faixa em execução (padrão off) |
overflowchecks on|off | verificação de transbordo em execução (padrão off) |
divcheck on|off | checagem de divisão por zero antes do sdiv (padrão on) |
allocsite on|off | 16 bytes na frente de cada bloco com o arquivo(linha) de quem o pediu, para o relatório de vazamento |
devirtualization on|off | virtual sem override vira chamada direta (padrão on) |
align N · minenumsize N | alinhamento dos records e largura mínima dos enumerados |
typedaddress on|off · pointermath | endereçamento tipado e aritmética de ponteiro |
RecordLayoutKind · ObjectLayoutKind Sequential|Auto | ordem dos campos (Auto autoriza reordenar) |
Record/Object PublishTrimmed, Record/Object/Global ParamsTrimmed | autorizações para os passes de otimização |
rtti on|off | grava o RTTI externo (.rtti.json) |
DefField, DefGetVisibility, DefSetVisibility | os acessores sintetizados por DefReadWrite |
discard 'ID' · cancel_discard 'ID' | a lista de descarte do discardable |
Outras diretivas ligam bibliotecas de objetos ({$ObjLink 'arquivo.lib'}, usada pela RTL para trazer o OpenSSL, o zlib e o pcre) e controlam o modo de código ({$code unsafe} / {$code safe}).
Dicas de otimização#
Palavras que dizem ao compilador o que a rotina promete. Ele pode usar isso para descartar chamadas e eliminar código.
| Palavra | Efeito |
|---|---|
pure | a chamada pode ser eliminada quando não fizer diferença: em posição de comando (resultado jogado fora), ela some na compilação |
force | na chamada: esta específica não pode ser eliminada (force TryStrToInt('1', X);) |
discardable 'ID' | rotina, bloco (begin discardable 'ID' … end), campo, classe inteira (end discardable 'ID';) ou região descartáveis por identificador; com {$config discard 'ID'} o trecho é descartado sem nem ser resolvido, e citá-lo de código vivo é erro (Declared(X) responde False) |
preserve | o item não pode ser alterado automaticamente (assinatura intocável para os cortes de parâmetros); a exclusão por não-uso continua valendo |
abi_win / abi_posix | força a ABI da rotina, seja qual for a plataforma alvo |
noreturn | a chamada não volta (exceção ou fim do processo); o que vem depois é inalcançável |
function TryStrToInt(const S: UString; out I: UInt128): Bool8; pure;
function Log(const S: UString): Bool8; discardable 'LOG';
procedure Halted(const ACode: Int32); noreturn;Existem ainda:
impure: o oposto depure. A chamada nunca é eliminada, em hipótese nenhuma.vital, no parâmetroout: sem ele, escrever no parâmetro do chamador seria efeito colateral e proibiria opure.vitaldiz que essa escrita é a única razão de a chamada existir, então ela vale enquanto alguém ler o que foi escrito. Um parâmetro também pode ser marcadopure.{$config PureImpure Ignore|Hint|Warning|Error|Fatal}(padrãoIgnore): exige que toda rotina se declarepureouimpure. O alcance é léxico, como o dodivcheck: quem quer a cobrança a pede, e a RTL inteira não responde junto.
function TryStrToUInt32(const pure S: UString; out vital I: UInt32): Bool8; pure;
function IncrementCounter: Bool8; impure;