Avaliação booleana em modo "curto-circuito" e operandos do tipo Variant
Escrito por Carlos B. Feitoza Filho | |
Categoria: Artigos | |
Categoria Pai: Addicted 2 Delphi! | |
Acessos: 6809 |
O modo curto-circuito de avaliação boolena é a forma padrão de avaliação lógica de todas as linguagens de programação. Basicamente o curto-circuito é um conceito que prega que uma expressão booleana complexa seja avaliada o mais rapidamente possível pelo compilador mediante o descarte de avaliações adicionais que são óbvias ou nulas dentro da referida expressão.
Se ainda assim não consegue entender o que é uma avaliação boolean em curto-circuito, dê uma olhada em Boolean short-circuit evaluation (Delphi compiler directive) e saiba que mesmo sem você saber da existência disso, provavelmente você usa, pois o curto-circuito é padrão do Delphi.
Chega de bla bla bla. Tenho a seguinte função:
function CanSelect(const aTableName: String): Boolean;
begin
Result := CurrentSession.Data.bo_superusuario
or CLDSPermissoes.Lookup('ENTIDADE',aTableName,'LER');
end;
A lógica é simples: A função vai retornar TRUE se eu for um superusuário ou se eu tiver permissão de leitura para a tabela passada no parâmetro aTableName.
Como a diretiva de compilação {$B-} está ativada como padrão, a lógica diz que se CurrentSession.Data.bo_superusuario for TRUE, o segundo operando nem mesmo vai ser avaliado pois apenas a condição do primeiro operando já é suficiente para saber o resultado da expressão booleana inteira. O que estava acontecendo é que o segundo operando, neste caso, estava sendo avaliado, mesmo quando o primeiro era TRUE, que, no meu caso gerava um exceção por conta de CLDSPermissoes estar inativo.
Após algumas pesquisas atentei para o fato do segundo operando (Lookup) retornar um dado Variant e por este motivo o compilador não tem condições de saber se ele é realmente um booleano ou não, causando a avaliação completa da expressão booleana mesmo no modo {$B-}. Ao meu ver isso é algum tipo de bug relacionado ao modo {$B-} o qual deveria "curtocircuitar" a expressão APENAS após a conversão do dado Variant, mas isso é outro assunto.
Após descobrir a causa do problema que impactava na minha lógica, foi simples resolver o problema. Simplesmente dei um cast no variant para Boolean, explicitando para o compilador que se trata de um operando BOOLEANO, assim:
function CanSelect(const aTableName: String): Boolean;
begin
Result := CurrentSession.Data.bo_superusuario
or Boolean(CLDSPermissoes.Lookup('ENTIDADE',aTableName,'LER'));
end;
Desta forma a lógica funciona como deve. Fica a dica!