A linguagem
static_assert e constantes de compilação
Premissas verificadas na compilação: tamanhos, restrições de tipos genéricos, constantes. Falhou, não compila.
O que é e por que existe#
Muita premissa de um programa é verificável antes de ele rodar: "este record tem 16 bytes", "este genérico só aceita tipos pequenos e não gerenciados", "esta tabela tem 10 entradas". static_assert escreve essa premissa no código, e o compilador a confere. Se for falsa, o programa nem compila.
static_assert(CLen = 9, 'tamanho do texto');
static_assert((CMax = 4) and (CAbs = 7)); // a mensagem é opcional
static_assert(IsManaged(UString) and IsRecord(TPoint));Onde vale#
- Na seção de declarações de
program,libraryemodule(depois devar/const/type, que a fecham). - Em declarações de rotina e como comando no corpo de uma rotina.
- No corpo de
recorde declass. - Dentro de genéricos: é reavaliado a cada instanciação, com o
Tjá trocado. É assim que um genérico restringe o argumento:
type
TSmall<T> = record
static_assert(SizeOf(T) <= 8, 'T grande demais');
static_assert(not IsManaged(T), 'T não pode ser gerenciado');
V: T;
end record;Constantes ampliadas#
A condição aceita as constantes ampliadas, que valem em qualquer expressão constante, não só no static_assert:
| Forma | Dá |
|---|---|
Length('cadeia constante') | o tamanho |
Abs, Min, Max | inteiros ou reais |
Chr(n) | cadeia de 1 caractere (n de 0 a 65535) |
Low/High de vetor estático | limite do primeiro índice |
SizeOf(T) | o tamanho em bytes |
Is(A, B), IsManaged, IsString, IsDynArray, IsObject, IsClass, IsRecord, IsManagedRecord, Declared | as funções de tipo do {$if}, como booleano constante |
program StaticAssert;
// Demo: `static_assert` verifica uma condição EM TEMPO DE COMPILAÇÃO. Se falhar, o programa nem compila.
// Serve para documentar e garantir premissas: tamanhos, restrições de tipos genéricos, constantes.
type
TPoint = record
X, Y: Int32;
end record;
TVec = array[3..12] of Int32;
// Em um genérico, o static_assert é reavaliado a cada instanciação: restringe o argumento T.
TSmall<T> = record
static_assert(SizeOf(T) <= 8, 'T grande demais');
static_assert(not IsManaged(T), 'T não pode ser gerenciado (cadeia, vetor dinâmico...)');
V: T;
end record;
const
// constantes de compilação aceitam várias funções: Length, Max, Abs, Chr, SizeOf, High, Low...
CText = 'olá mundo';
CLen = Length(CText);
CMax = Max(3, 4);
CAbs = Abs(-7);
CChr = Chr(65);
CSize = SizeOf(TPoint) * 2;
CCount = High(TVec) - Low(TVec) + 1;
// no nível do módulo
static_assert(CLen = 9, 'tamanho do texto');
static_assert((CMax = 4) and (CAbs = 7)); // a mensagem é opcional
static_assert(CChr = 'A');
static_assert(CCount = 10, 'TVec tem 10 elementos');
static_assert(IsManaged(UString) and IsRecord(TPoint));
procedure Run;
begin
static_assert(SizeOf(Int64) = 8); // também vale como comando
WriteLn('Length=', CLen, ' Max=', CMax, ' Abs=', CAbs, ' Chr=', CChr, ' SizeOf*2=', CSize, ' Count=', CCount);
end procedure;
var
LSmall: TSmall<Int32>; // TSmall<UString> ou TSmall<Int128> não compilariam
begin
LSmall.V := 5;
Run;
WriteLn('TSmall<Int32>.V = ', LSmall.V);
end.Length=9 Max=4 Abs=7 Chr=A SizeOf*2=16 Count=10
TSmall<Int32>.V = 5Demo 49. Todas as asserções passam; trocar SizeOf(Int64) = 8 por = 4 ou instanciar TSmall<UString> faria o compilador recusar.
Erros típicos#
- Condição falsa:
FE1484, na posição dostatic_assert. Com mensagem:static_assert falhou: Int64 tem 8 bytes; sem:static_assert falhou. Numa instanciação genérica, a posição volta ao fonte e a mensagem leva o sufixo[na instanciação de TSmall__Int128]. - O erro é registrado como um
{$message error}: a leitura continua e os demais erros também saem. - Condição que não é constante: "a condição não pôde ser avaliada em tempo de compilação". Condição de cadeia é erro; inteiro diferente de zero vale verdadeiro.
- Limites: nada de função do usuário;
Lengthsó de cadeia constante;SizeOf(variável)não é constante.and/orseguem a precedência do avaliador: comparações precisam de parênteses,(A = 1) and (B = 2). static_asserté palavra contextual (início de declaração, comando ou membro, seguida de().