Pergunta

Eu fiquei realmente impressionado com este delphi dois forro usando a IFThen função de Matemática.pas.No entanto, ele avalia o DB.ReturnFieldI primeiro, o que é lamentável, porque eu preciso chamar DB.primeiro, para obter o primeiro registro.

DB.RunQuery('select awesomedata1 from awesometable where awesometableid = "great"');
result := IfThen(DB.First = 0, DB.ReturnFieldI('awesomedata1'));

(como um inútil esclarecimento, porque eu tenho tantas boas respostas já.Eu esqueci de mencionar que 0 é o código que DB.Primeiro retorna se ele tem alguma coisa, pode não ter feito sentido contrário)

Obviamente, este não é um negócio tão grande, como eu poderia fazê-lo funcionar com cinco robusto forros.Mas tudo o que eu preciso para que este trabalho é para Delphi para avaliar DB.primeiro e DB.ReturnFieldI segundo.Eu não quero mudar de matemática.pas e eu não acho que isso garante-me fazer uma sobrecarregado ifthen porque não há como 16 ifthen funções.

Apenas deixe-me saber que a directiva de compilador é, se há uma maneira melhor para fazer isto, ou se não há nenhuma maneira de fazer isso e qualquer pessoa cujo procedimento é chamada db.primeiro e cegamente recuperar a primeira coisa que ele encontra não é um verdadeiro programador.

Foi útil?

Solução

A ordem de avaliação das expressões é comumente não definido.(C e C++ são da mesma forma.Java é sempre avaliada da esquerda para a direita). O compilador oferece nenhum controle sobre ele.Se você precisa de duas expressões a serem avaliados em uma ordem específica, em seguida, escrever o seu código de forma diferente.Eu não teria realmente se preocupar com o número de linhas de código.As linhas são baratos;use como muitos como você precisa.Se você encontrar-se usando esse padrão, muitas vezes, escrever uma função que envolve tudo:

function GetFirstIfAvailable(DB: TDatabaseObject; const FieldName: string): Integer;
begin
  if DB.First = 0 then
    Result := DB.ReturnFieldI(FieldName)
  else
    Result := 0;
end;

Seu código original, provavelmente não teria sido o que você queria, mesmo se a ordem de avaliação foram diferentes.Mesmo se DB.First não era igual a zero, a chamada para ReturnFieldI ainda seria avaliada.Todos os parâmetros reais são avaliadas antes de chamar a função que usa-los.

Mudando De Matemática.pas não iria ajudá-lo de qualquer maneira.Ele não tem controle sobre a ordem de seus parâmetros reais são avaliados.Pelo tempo de vê-los, eles já foram avaliados para baixo para um valor Booleano e um número inteiro;eles não são executáveis expressões mais.


A convenção de chamada pode afetar a ordem de avaliação, mas não há ainda nenhuma garantia.A ordem que os parâmetros são enviados para a pilha não precisa coincidir com a ordem em que esses valores foram determinados.De fato, se você achar que stdcall ou cdecl dá-lhe a sua desejada ordem de avaliação (da esquerda para a direita) e, em seguida, eles estão sendo avaliados na ordem inversa do que eles são passados com.

O pascal convenção de chamada passa argumentos para a esquerda-para-direita na pilha.Isso significa que a esquerda argumento é aquele no fundo da pilha, e o mais à direita argumento está no topo, logo abaixo do endereço de retorno.Se o IfThen função utilizada que a convenção de chamada, existem várias maneiras de como o compilador poderia conseguir que a pilha de layout:

  1. A maneira que você espera, o que é que cada argumento é avaliado e enviado de imediato:

    push (DB.First = 0)
    push DB.ReturnFieldI('awesomedata1')
    call IfThen
    
  2. Avaliar argumentos da direita para a esquerda e armazenar os resultados em temporários até que eles são enviados:

    tmp1 := DB.ReturnFieldI('awesomedata1')
    tmp2 := (DB.First = 0)
    push tmp2
    push tmp1
    call IfThen
    
  3. Alocar espaço de pilha primeiro, e avaliar em qualquer ordem é conveniente:

    sub esp, 8
    mov [esp], DB.ReturnFieldI('awesomedata1')
    mov [esp + 4], (DB.First = 0)
    call IfThen
    

Observe que IfThen recebe o argumento de valores na mesma ordem em todos os três casos, mas as funções não são, necessariamente, chamados por essa ordem.

O padrão de registrar convenção de chamada também passa argumentos para a esquerda-para-direita, mas os três primeiros argumentos que se encaixam são passados em registros.Os registradores usados para passar argumentos, porém, são também os registros mais comumente usados para a avaliação de expressões intermédias.O resultado de DB.First = 0 precisava ser passado no registrador EAX, mas o compilador também necessário que se registrar para chamar ReturnFieldI e para chamar First.Foi, provavelmente, um pouco mais conveniente para avaliar a segunda função primeira, como este:

call DB.ReturnFieldI('awesomedata1')
mov [ebp - 4], eax // store result in temporary
call DB.First
test eax, eax
setz eax
mov edx, [ebp - 4]
call IfThen

Outra coisa a salientar é que o primeiro argumento é um composto de expressão.Há uma chamada de função e uma comparação.Não há nada a garantia de que essas duas partes são executadas consecutivamente.O compilador pode obter a função de chamadas para fora do caminho primeiro chamando First e ReturnFieldI, e depois comparar a First valor de retorno contra zero.

Outras dicas

o Convenção de Chamada afeta a maneira como eles são avaliados.
Não há um compilador definido para controlar isso.

Pascal é a convenção de chamada que você teria que usar para obter esse comportamento.

Embora eu pessoalmente nunca dependa desse tipo de comportamento.

O programa de exemplo a seguir demonstra como isso funciona.

program Project2;
{$APPTYPE CONSOLE}
uses SysUtils;

function ParamEvalTest(Param : Integer) : Integer;
begin
  writeln('Param' + IntToStr(Param) + ' Evaluated');
  result := Param;
end;

procedure TestStdCall(Param1,Param2 : Integer); stdCall;
begin
  Writeln('StdCall Complete');
end;

procedure TestPascal(Param1,Param2 : Integer); pascal;
begin
  Writeln('Pascal Complete');
end;

procedure TestCDecl(Param1,Param2 : Integer); cdecl;
begin
  Writeln('CDecl Complete');
end;

procedure TestSafecall(Param1,Param2 : Integer); safecall;
begin
  Writeln('SafeCall Complete');
end;

begin
  TestStdCall(ParamEvalTest(1),ParamEvalTest(2));
  TestPascal(ParamEvalTest(1),ParamEvalTest(2));
  TestCDecl(ParamEvalTest(1),ParamEvalTest(2));
  TestSafeCall(ParamEvalTest(1),ParamEvalTest(2));
  ReadLn;
end.

Isso exigiria que você escrevesse suas próprias funções.

Se você realmente quer que este seja um revestimento único, você realmente pode fazer isso em Delphi. Eu só acho que parece feio.

If (DB.First = 0) then result :=  DB.ReturnFieldI('awesomedata1') else result := 0;

Você não pode alterar sua consulta para ter apenas um resultado, então evite fazer o comando 'primeiro'? Assim como :

SELECT TOP 1 awesomedata1 from awesometable 

No acesso ...

AFAIK não há nenhuma diretiva de compilador para controlar isso.A menos que você use o stdcall/cdecl/safecall convenções, os parâmetros são passados esquerda para a direita na pilha, mas porque o padrão de registrar convenção pode passar parâmetros em seus registros, bem como, pode acontecer que um parâmetro é calculado, mais tarde, colocar em registrar-se antes da chamada.E porque apenas a registar a ordem é fixa (EAX, EDX, ECX) para os parâmetros que qualificam, os registradores podem ser carregados em qualquer ordem.Você pode tentar forçar um "pascal" convenção de chamada (você precisa reescrever a função, de qualquer forma), mas IMHO é sempre perigoso confiar em tal tipo de código, se o compilador pode não garante explicitamente a ordem de avaliação.E a imposição de uma ordem de avaliação pode reduzir significativamente o número de otimizações disponíveis.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top