Frage

Ich war wirklich mit diesem delphi zwei Liner beeindruckte die ifthen Funktion von Math.pas verwenden. Allerdings wertet er die DB.ReturnFieldI erste, was schade ist, weil ich DB.first anrufen müssen den ersten Datensatz zu erhalten.

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

(als sinnlos Klärung, denn ich habe schon so viele gute Antworten. Ich habe vergessen zu erwähnen, dass 0 ist der Code, dass DB.First zurückkehrt, wenn es etwas in ihm bekam ist, hätte keinen Sinn anders gemacht)

Natürlich ist dies nicht so eine große Sache, wie ich konnte es mit fünf robusten Liner arbeiten. Aber alles was ich brauche dafür zu arbeiten ist für Delphi DB.first zu bewerten ersten und zweiten DB.ReturnFieldI. Ich will nicht math.pas ändern, und ich glaube nicht, das garantiert mir eine überladene ifthen machen, weil es wie 16 ifthen Funktionen ist.

Lassen Sie mich wissen, was der Compiler-Direktive ist, wenn es eine noch bessere Möglichkeit, dies zu tun, oder wenn es keine Möglichkeit, dies und alle Personen, deren Verfahren zu tun ist db.first anrufen und blind das erste, was er abrufen Funde ist kein echter Programmierer.

War es hilfreich?

Lösung

Die Reihenfolge der Auswertung von Ausdrücken ist in der Regel undefined . (C und C ++ sind die gleiche Weise. Java wertet immer von links nach rechts.) Der Compiler bietet keine Kontrolle darüber. Wenn Sie zwei Ausdrücke in einer bestimmten Reihenfolge ausgewertet werden, dann schreiben Sie Ihren Code anders. Ich würde über die Anzahl der Codezeilen nicht wirklich Sorgen machen. Die Linien sind billig; Verwenden Sie so viele, wie Sie benötigen. Wenn Sie sich mit diesem Muster oft finden, schreiben Sie eine Funktion, dass Wraps es auf den Punkt:

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

Ihr Original-Code würde wahrscheinlich nicht gewesen, was man wollte, auch wenn Auswerteauftrag unterschiedlich waren. Selbst wenn DB.First nicht gleich Null ist, der Anruf zu ReturnFieldI noch ausgewertet werden würde. Alle aktuellen Parameter werden vollständig ausgewertet, bevor die Funktion, dass nutzt sie aufgerufen wird.

Math.pas ändern würden Sie sowieso nicht helfen. Es steuert nicht welcher Reihenfolge die aktuellen Parameter ausgewertet Durch die Zeit, die sie sieht, haben sie bereits in einen Booleschen Wert ausgewertet unten worden und eine ganze Zahl. sie sind nicht ausführbar Ausdrücke mehr.


Die Aufrufkonvention kann Auswerteauftrag beeinflussen, aber es gibt immer noch keine Garantie. Die Reihenfolge, dass die Parameter auf den Stapel geschoben werden muss nicht um den Auftrag zu entsprechen, in denen diese Werte ermittelt wurden. Tatsächlich, wenn Sie diesen stdcall oder cdecl gibt Ihnen gewünschte Auswertungsreihenfolge (von links nach rechts) finden, dann sind sie in der umgekehrten Reihenfolge ausgewertet den einer sind sie mit übergeben.

Die pascal Aufrufkonvention geht Argumente von links nach rechts auf dem Stapel. Das heißt, die am weitesten links stehende Argument ist der einer an der Unterseite des Stapels und das am weitesten rechts stehende Argument ist an der Oberseite, direkt unterhalb der Rücksprungadresse. Wenn die IfThen verwendete Funktion Konvention, dass Aufruf, gibt es mehrere Möglichkeiten der Compiler, dass die Stapel-Layout erreichen könnte:

  1. Wie Sie erwarten, das ist, dass jedes Argument ausgewertet wird und schiebt sofort:

    push (DB.First = 0)
    push DB.ReturnFieldI('awesomedata1')
    call IfThen
    
  2. Ausrechnen Argumente von rechts nach links und die Ergebnisse in Provisorien, bis sie gedrückt sind:

    tmp1 := DB.ReturnFieldI('awesomedata1')
    tmp2 := (DB.First = 0)
    push tmp2
    push tmp1
    call IfThen
    
  3. Weisen Stapelspeicher erster und bewerten in beliebiger Reihenfolge ist bequem:

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

Beachten Sie, dass IfThen erhält die Argumentwerte in derselben Reihenfolge in allen drei Fällen, aber die Funktionen sind nicht unbedingt in dieser Reihenfolge genannt.

Die Standard-Aufrufkonvention register auch gibt Argumente von links nach rechts, aber die ersten drei Argumente, die in den Registern passen weitergegeben werden. Die Register verwendet, um Argumente zu übergeben, aber sind auch die Register am häufigsten zur Auswertung Zwischen Ausdrücke verwendet. Das Ergebnis DB.First = 0 benötigt im EAX-Register übergeben werden, aber der Compiler auch, dass Register benötigt für ReturnFieldI Aufruf und für den Aufruf First. Es war wohl ein wenig bequemer, die zweite Funktion zuerst zu bewerten, wie folgt aus:

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

Ein andere Sache, darauf zu hinweisen, dass Ihr erstes Argument ist eine Verbindung, Ausdruck. Es gibt einen Funktionsaufruf und ein Vergleich. Es gibt nichts zu garantieren, dass diese beiden Teile nacheinander durchgeführt werden. Der Compiler kann die Funktion ruft aus dem Weg zuerst von First und ReturnFieldI nennen, und danach vergleicht den First Rückgabewert gegen Null.

Andere Tipps

Die Aufrufkonvention die Art und Weise beeinflusst sie ausgewertet werden.
Es gibt nicht einen Compiler definieren diese zu steuern.

Pascal ist die Aufrufkonvention Sie müßten verwenden, um dieses Verhalten zu erhalten.

Auch wenn ich persönlich würde nie auf diese Art von Verhalten abhängig sind.

Das folgende Beispielprogramm zeigt, wie das funktioniert.

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.

Dies würden Sie benötigen, um Ihre eigenen IfThen Funktionen zu schreiben.

Wenn Sie wirklich wollen, dass diese ein Motto sein, wirklich Sie, dass in Delphi tun. Ich denke nur, es sieht hässlich aus.

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

Sie können nicht Ihre Abfrage ändern nur ein Ergebnis haben, so vermeiden Sie den ‚ersten‘ Befehl zu tun? Genau wie:

SELECT TOP 1 awesomedata1 from awesometable 

In Access ...

AFAIK gibt es kein Compiler-Direktive dies zu kontrollieren. Es sei denn, Sie verwenden die stdcall / cdecl / Safecall-Konventionen, werden Parameter übergeben auf dem Stapel links nach rechts, sondern weil die Standardregisterkonvention als auch Parameter in den Registern passieren kann, könnte es passieren, dass ein Parameter später eine Put in einem Register berechnet kurz vor dem Anruf. Und weil nur das Register Reihenfolge festgelegt ist (EAX, EDX, ECX) für Parameter, die in Frage kommen, Register können in beliebiger Reihenfolge geladen werden. Sie könnten versuchen, eine „pascal“ Aufrufkonvention zu erzwingen (müssen Sie die Funktion neu zu schreiben, sowieso), aber IMHO ist immer gefährlich auf einem solchen Art von Code zu verlassen, wenn der Compiler nicht explizit die Reihenfolge der Auswertung garantieren kann. Und eine Bewertung, um zur Einführung kann die Anzahl der verfügbaren Optimierungen deutlich reduzieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top