Pregunta

Yo estaba muy impresionado con este delphi dos revestimiento utilizando la función de ifthen Math.pas. Sin embargo, se evalúa primero la DB.ReturnFieldI, lo cual es lamentable porque necesito llamar DB.first para obtener el primer registro.

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

(como una aclaración sin sentido, porque tengo tantas respuestas buenas ya. Me olvidé de mencionar que 0 es el código que vuelve DB.First si tiene algo en él, no podría haber tenido sentido de otra manera)

Obviamente, esto no es un gran problema, ya que podía hacer que funcione con cinco trazadores de líneas robustas. Pero todo lo necesario para que esto funcione es para Delphi para evaluar DB.first primera y segunda DB.ReturnFieldI. No quiero cambiar math.pas y yo no creo que esto garantiza mí hacer una ifthen sobrecargado porque hay como 16 ifthen funciones.

Al igual que me haga saber lo que la directiva del compilador es, si hay una mejor manera de hacer esto, o si no hay manera de hacer esto y cualquier persona cuyo procedimiento es llamar db.first y ciegamente recuperar la primera cosa que él hallazgos no es un verdadero programador.

¿Fue útil?

Solución

El orden de evaluación de expresiones es comúnmente Indefinido . (C y C ++ son de la misma manera. Java siempre se evalúa de izquierda a derecha.) Las ofertas del compilador ningún control sobre él. Si necesita dos expresiones se evalúan en un orden específico, a continuación, escribir el código diferente. Realmente no lo preocuparse por el número de líneas de código. Las líneas son baratos; usar tantos como sea necesario. Si usted se encuentra utilizando este patrón menudo, escribir una función que lo envuelve todo para arriba:

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

Su código original probablemente no habría sido lo que quería, incluso si el orden de evaluación eran diferentes. Incluso si DB.First fue no igual a cero, la llamada a ReturnFieldI seguiría siendo evaluada. Todos los parámetros actuales se hayan evaluado plenamente antes de invocar la función que utiliza ellos.

Cambiar Math.pas no le serviría de todos modos. No controla qué orden sus parámetros actuales se evalúan en el momento en que los ve, que ya han sido evaluados hasta un valor booleano y un entero.; son expresiones no ejecutables más.


La convención de llamada puede afectar orden de evaluación, pero no hay ninguna garantía. El orden en que los parámetros se insertan en la pila no tiene que coincidir con el orden en el que se determinaron estos valores. De hecho, si usted encuentra que stdcall o cdecl le da el orden de evaluación deseada (de izquierda a derecha), entonces ellos están siendo evaluados en el orden inverso de la que están pasados ??con.

El Pascal convención de llamada pasa argumentos de izquierda a derecha en la pila. Eso significa que el argumento más a la izquierda es el que está en la parte inferior de la pila, y el argumento más a la derecha es en la parte superior, justo debajo de la dirección de retorno. Si la función IfThen usó esa convención de llamada, hay varias maneras de que el compilador podría lograr que la disposición de pila:

  1. La forma esperada, y es que cada argumento es evaluado y empujado inmediatamente:

    push (DB.First = 0)
    push DB.ReturnFieldI('awesomedata1')
    call IfThen
    
  2. Evaluar los argumentos de derecha a izquierda y almacenar los resultados en los temporales hasta que son empujados:

    tmp1 := DB.ReturnFieldI('awesomedata1')
    tmp2 := (DB.First = 0)
    push tmp2
    push tmp1
    call IfThen
    
  3. Asignar espacio de pila en primer lugar, y evaluar en el orden que sea conveniente:

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

Tenga en cuenta que IfThen recibe los valores de los argumentos en el mismo orden en los tres casos, pero las funciones se denominan no necesariamente en ese orden.

La convención de llamada de registro por defecto también pasa argumentos de izquierda a derecha, y los tres primeros argumentos que se ajustan se pasan en registros. Los registros utilizados para pasar argumentos, sin embargo, son también los registros más comúnmente utilizados para la evaluación de expresiones intermedias. El resultado de DB.First = 0 necesitaba ser aprobada en el registro EAX, pero el compilador también necesitaba ese registro para llamar ReturnFieldI y para llamar First. Probablemente fue un poco más conveniente evaluar la segunda función en primer lugar, de esta manera:

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

Otra cosa a destacar es que su primer argumento es una expresión compuesta. Hay una llamada de función y una comparación. No hay nada que garantía de que esas dos partes se realizan de forma consecutiva. El compilador podría obtener la función llama a cabo de la manera en primer lugar llamando First y ReturnFieldI, y después comparar el valor de retorno First contra cero.

Otros consejos

La convención llamando afecta a la forma en que se evalúan.
No es un compilador definen para controlar esto.

Pascal es la convención de llamada que tendría que utilizar para conseguir este comportamiento.

A pesar de que personalmente nunca dependería de este tipo de comportamiento.

El siguiente programa de ejemplo muestra cómo funciona este.

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.

Esto requeriría que para escribir sus propias funciones ifthen.

Si realmente quiere que esto sea un chiste que realmente puede hacer que en Delphi. Sólo creo que se ve feo.

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

No puedes cambiar la consulta de tener sólo un resultado así que evite hacer el comando 'Primera'? Al igual que:

SELECT TOP 1 awesomedata1 from awesometable 

En Access ...

Que yo sepa no hay ninguna directiva de compilación para controlar esto. A menos que utilice la stdcall / cdecl / Safecall convenciones, se pasan los parámetros de izquierda a derecha en la pila, pero debido a la convención de registro por defecto puede pasar parámetros en los registros, así, podría suceder que un parámetro se calcula después un puesto en un registro justo antes de la llamada. Y debido a que sólo el orden de registro es fijo (EAX, EDX, ECX) para los parámetros que califican, los registros se pueden cargar en cualquier orden. Se podría tratar de forzar una convención de llamada "Pascal" (que había necesidad de volver a escribir la función, de todos modos), pero en mi humilde opinión siempre es peligroso confiar en este tipo de código, si el compilador no puede garantizar de forma explícita el orden de evaluación. Y se establece un orden de evaluación puede reducir en gran medida el número de optimizaciones disponibles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top