質問

Math.pasのIfthen関数を使用して、このDelphi Two Linerに本当に感銘を受けました。ただし、最初にDB.Returnfieldiを評価します。これは、最初のレコードを取得するにはDB.Firstに電話する必要があるため、残念です。

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

(無意味な明確化として、私はすでに非常に多くの良い答えを持っているので、0が何かが入っている場合、db.firstが返すコードであることを忘れていました。

明らかに、これはそれほど大したことではありません。5つの堅牢なライナーで動作させることができたからです。しかし、これが機能するために必要なのは、DelphiがDB.First FirstとDB.Returnfieldiの2番目を評価することです。私はMath.pasを変更したくありません。これは、16のIfthenのようなものがあるため、過負荷になった場合にこれを保証するとは思いません。

コンパイラディレクティブが何であるか、これを行うためのさらに良い方法がある場合、またはこれを行う方法がない場合、およびdb.firstに電話して盲目的に彼が見つけたものを盲目的に取得することである場合は、本当のプログラマー。

役に立ちましたか?

解決

表現の評価順序は一般的です 未定義. 。 (CとC ++は同じ方法です。Javaは常に左から右から評価します。)コンパイラはそれを制御しません。特定の順序で2つの式を評価する必要がある場合は、コードを別の方法で書き込みます。コードの行の数については本当に心配しません。線は安いです。必要なだけ使用してください。このパターンを頻繁に使用していることに気付いた場合は、それをすべてまとめる関数を書きます。

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

評価の順序が異なっていても、元のコードはおそらくあなたが望んでいたものではなかったでしょう。もしそれでも DB.First そうではありませんでした ゼロに等しく、コール ReturnFieldI まだ評価されます。すべての実際のパラメーターは、それらを使用する関数を呼び出す前に完全に評価されます。

とにかくmath.pasを変更することはあなたを助けません。実際のパラメーターが評価される順序を制御しません。それらを見るまでに、それらはすでにブール値と整数まで評価されています。それらはもう実行可能な表現ではありません。


通話条約は評価順序に影響を与える可能性がありますが、まだ保証はありません。パラメーターがスタックに押し込まれるという順序は、それらの値が決定された順序と一致させる必要はありません。確かに、stdcallまたはcdeclがあなたの希望の評価順序(左から右へ)を提供することがわかった場合、それらはで評価されています 逆注文 彼らが渡されたものの。

パスカル コンベンションの呼び出しは、スタックの左に引数を渡します。つまり、左端の引数はスタックの下部にあるものであり、右端の引数は、戻りアドレスのすぐ下にある上部にあります。場合 IfThen コンベンションを呼び出す機能を使用して、コンパイラがそのスタックレイアウトを達成できるいくつかの方法があります。

  1. あなたが期待する方法、つまり各議論はすぐに評価され、プッシュされることです。

    push (DB.First = 0)
    push DB.ReturnFieldI('awesomedata1')
    call IfThen
    
  2. 引数を左に評価し、結果をプッシュするまで一時的なものに保存します。

    tmp1 := DB.ReturnFieldI('awesomedata1')
    tmp2 := (DB.First = 0)
    push tmp2
    push tmp1
    call IfThen
    
  3. 最初にスタックスペースを割り当て、便利な順序で評価します。

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

それに注意してください IfThen 受信します 引数は3つすべての場合に同じ順序で値値を値しますが、関数は必ずしもその順序で呼び出されるわけではありません。

デフォルトのレジスタコール条約は、左から右への引数も渡されますが、フィットする最初の3つの引数はレジスタで渡されます。ただし、引数を渡すために使用されるレジスタは、中間式の評価に最も一般的に使用されるレジスタでもあります。結果として DB.First = 0 EAXレジスタで渡す必要がありましたが、コンパイラは通話用のレジスタも必要でした ReturnFieldI そして呼び出しのために First. 。このように、最初に2番目の関数を評価するのはおそらくもう少し便利でした。

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

指摘するもう1つのことは、あなたの最初の議論が複合式であるということです。関数呼び出しと比較があります。これらの2つの部分が連続して実行されることを保証するものは何もありません。コンパイラは、最初に呼び出して関数の呼び出しを取得する場合があります FirstReturnFieldI, 、そしてその後を比較します First ゼロに対して値を返します。

他のヒント

電話会議 評価の方法に影響します。
これを制御するために定義するコンパイラはありません。

Pascal この動作を得るために使用しなければならない呼び出し条約です。

私は個人的にこのタイプの行動に依存することはありませんが。

次の例プログラムは、これがどのように機能するかを示しています。

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.

これには、独自の関数を書く必要があります。

あなたが本当にこれを1つのライナーにしたいなら、あなたは本当にデルファイでそれをすることができます。醜く見えると思います。

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

クエリを1つだけの結果を得るように変更できないので、「最初の」コマンドを実行することは避けますか?と同じように :

SELECT TOP 1 awesomedata1 from awesometable 

アクセス...

AFAIKこれを制御するコンパイラディレクティブはありません。 stdcall/cdecl/safecallコンベンションを使用しない限り、パラメーターはスタック上で左に渡されますが、デフォルトのレジスタコンベンションもレジスタ内のパラメーターを渡すことができるため、パラメーターは後でレジスタのプットを計算します。電話の直前。また、登録順序のみが修正されているため(EAX、EDX、ECX)、適格なパラメーターに対して、レジスタを任意の順序でロードできます。 「Pascal」の呼び出し条約を強制しようとすることができます(とにかく関数を書き換える必要があります)が、コンパイラが評価の順序を明示的に保証できない場合、私見はそのような種類のコードに依存することは常に危険です。評価命令を課すと、利用可能な最適化の数が大幅に減少する場合があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top