Fenner
A linguagem

Regiões, forward e initialization

Organizar um módulo com public region, esconder o resto, declarar rotinas adiantadas e a ordem de initialization e finalization.

O que é e por que existe#

Um módulo .fxm não tem interface/implementation: cada declaração diz se é public (visível a quem usa o módulo) ou private (a omissão). Escrever public em cada item cansa; a região agrupa vários e os torna públicos de uma vez. E para recursão mútua entre rotinas, forward declara só o cabeçalho.

Fenner
module Parity;

var GCalls: Int32;                         // sem marca: private

private function IsOdd(N: Int32): Bool8; forward;     // só o cabeçalho

public region 'Parity.Public';             // tudo daqui até "end region" é público
type
  TParityReport = record
    Value: Int32;
    Parity: UString;
  end record;

function IsEven(N: Int32): Bool8;
begin
  Inc(GCalls);
  if N = 0 then Exit(True);
  Exit(IsOdd(N - 1));
end function;
end region;

function IsOdd(N: Int32): Bool8;           // fora da região: continua private
begin
  Inc(GCalls);
  if N = 0 then Exit(False);
  Exit(IsEven(N - 1));
end function;
  • public region 'rótulo'; … end region;: o rótulo serve à IDE. Fora de região, escreva public em cada item.
  • forward: declara o cabeçalho; o corpo vem depois. Permite recursão mútua (IsEven e IsOdd).
  • O programa só enxerga o que o módulo declarou público: citar GCalls ou IsOdd no programa seria erro de compilação.

initialization e finalization#

initialization roda uma vez, quando o programa inicia (antes do begin principal); finalization roda ao encerrar, em ordem inversa à da inicialização. A ordem das mensagens no demo mostra isso:

Regions.fxp
program Regions;
// Demo: organização de módulos. Veja Parity.fxm: `public region`, `private`, `forward`,
// `initialization` e `finalization`. O programa só enxerga o que o módulo declarou público.

uses System.Remainder, Parity;

var
  LReport: TParityReport;
begin
  WriteLn('[Regions] início do programa');
  LReport := Describe(7);
  WriteLn(LReport.Value, ' é ', LReport.Parity);
  WriteLn('10 é par? ', IsEven(10));
  WriteLn('chamadas feitas dentro do módulo: ', CallCount);
  // GCalls e IsOdd são private em Parity: citá-los aqui seria erro de compilação
  WriteLn('[Regions] fim do programa');
end.
Parity.fxm
module Parity;

// Demo de organização de um módulo: `public region`, `private`, `forward`, initialization/finalization.

uses System.Remainder;

var
  GCalls: Int32;     // sem marca: a omissão num módulo é private (invisível fora daqui)

// `forward` declara só o cabeçalho; o corpo vem depois. Permite recursão mútua (IsEven <-> IsOdd).
private function IsOdd(N: Int32): Bool8; forward;

// `public region 'rótulo'; ... end region;` agrupa declarações e torna PÚBLICO tudo que está dentro
// (o rótulo é para a IDE). Fora de região, é preciso escrever `public` em cada item.
public region 'Parity.Public';

type
  TParityReport = record
    Value: Int32;
    Parity: UString;
  end record;

function IsEven(N: Int32): Bool8;
begin
  Inc(GCalls);
  if N = 0 then
    Exit(True);
  Exit(IsOdd(N - 1));
end function;

function Describe(N: Int32): TParityReport;
begin
  Result.Value := N;
  if IsEven(N) then
    Result.Parity := 'par'
  else
    Result.Parity := 'ímpar';
end function;

function CallCount: Int32;
begin
  Exit(GCalls);
end function;

end region;

// fora da região: continua private, mas fecha a recursão mútua iniciada pelo forward
function IsOdd(N: Int32): Bool8;
begin
  Inc(GCalls);
  if N = 0 then
    Exit(False);
  Exit(IsEven(N - 1));
end function;

// roda uma vez, quando o programa inicia (antes do begin principal)
initialization
  GCalls := 0;
  WriteLn('[Parity] inicializado');

// roda ao encerrar, em ordem inversa à da inicialização
finalization
  WriteLn('[Parity] finalizado após ', GCalls, ' chamadas');

end.
Saída
[Parity] inicializado
[Regions] início do programa
7 é ímpar
10 é par? TRUE
chamadas feitas dentro do módulo: 19
[Regions] fim do programa
[Parity] finalizado após 19 chamadas

Demo 54. [Parity] inicializado, depois o programa, depois [Parity] finalizado com a contagem de chamadas.

Armadilhas#

  • O corpo da rotina forward vem depois, no mesmo módulo; sem ele o compilador acusa "declarado mas não implementado" (FE0960). Se a intenção é só declarar, use external, forward ou abstract.
  • A região só torna públicos os itens declarados dentro dela; os que ficam depois do end region voltam a ser privados.
  • Em módulo em ciclo, a ordem das initialization é a da conclusão da leitura (veja Módulos que se citam).

Veja também#