Question

je suis vraiment impressionné par ce delphi deux doublure en utilisant la fonction IfThen de Math.pas. Toutefois, il évalue la DB.ReturnFieldI d'abord, ce qui est regrettable parce que je dois appeler DB.first pour obtenir le premier enregistrement.

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

(comme une clarification inutile, parce que j'ai tant de bonnes réponses déjà. J'ai oublié de mentionner que 0 est le code que le rendement DB.First si elle a quelque chose en elle, peut-être pas en sens contraire)

Il est évident que ce n'est pas une grosse affaire, que je pouvais le faire fonctionner avec cinq paquebots robustes. Mais tout ce que je besoin pour que cela fonctionne est pour Delphi pour évaluer DB.first première et seconde DB.ReturnFieldI. Je ne veux pas changer math.pas et je ne pense pas que cela justifie de me faire un ifthen surchargé parce qu'il ya comme 16 ifthen fonctions.

Laissez-moi savoir ce que la directive du compilateur est, s'il y a une meilleure façon de le faire, ou s'il n'y a aucun moyen de le faire et toute personne dont la procédure est d'appeler db.first et récupérer aveuglément la première chose qu'il ne sont pas découvertes un programmeur réel.

Était-ce utile?

La solution

L'ordre d'évaluation des expressions est généralement non défini . (C et C ++ sont de la même façon. Java toujours évalue. De gauche à droite) Les offres du compilateur pas de contrôle. Si vous avez besoin de deux expressions à évaluer dans un ordre spécifique, puis écrivez votre code différemment. Je vous inquiétez pas vraiment sur le nombre de lignes de code. Les lignes ne coûtent pas cher; utiliser autant que vous avez besoin. Si vous vous trouvez en utilisant ce modèle souvent, écrire une fonction qui enveloppe le tout:

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

Votre code d'origine aurait probablement pas été ce que vous vouliez, même si l'ordre d'évaluation étaient différents. Même si DB.First n'a pas été égal à zéro, l'appel à ReturnFieldI serait encore évalué. Tous les paramètres réels sont entièrement évalués avant d'appeler la fonction qui utilise les.

La modification Math.pas ne vous aiderait pas de toute façon. Il ne contrôle pas quel ordre ses paramètres réels sont évalués par le temps qu'il les voit, ils ont déjà été évalués à une valeur booléenne et un entier. ils sont des expressions non exécutables plus.


La convention d'appel peut affecter l'ordre d'évaluation, mais il n'y a toujours pas de garantie. L'ordre que les paramètres sont poussés sur la pile n'a pas besoin de correspondre à l'ordre dans lequel ces valeurs ont été déterminées. En effet, si vous trouvez que stdcall ou cdecl vous donne votre ordre d'évaluation souhaité (de gauche à droite), ils sont en cours d'évaluation dans le l'ordre inverse de celui qu'ils sont passés avec.

pascals convention d'appel transmet des arguments de gauche à droite sur la pile. Cela signifie que l'argument est le plus à gauche un en bas de la pile, et l'argument est plus à droite en haut, juste au-dessous l'adresse de retour. Si la fonction de IfThen a utilisé cette convention d'appel, il existe plusieurs façons le compilateur peut réaliser cette mise en page de la pile:

  1. La façon dont vous attendez, ce qui est que chaque argument est évalué et poussé immédiatement:

    push (DB.First = 0)
    push DB.ReturnFieldI('awesomedata1')
    call IfThen
    
  2. évaluer les arguments de droite à gauche et à stocker les résultats dans jusqu'à ce qu'ils soient temporaires poussés:

    tmp1 := DB.ReturnFieldI('awesomedata1')
    tmp2 := (DB.First = 0)
    push tmp2
    push tmp1
    call IfThen
    
  3. Allouer premier espace de pile, et d'évaluer dans l'ordre est pratique:

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

Notez que IfThen reçoit les valeurs des arguments dans le même ordre dans les trois cas, mais les fonctions ne sont pas nécessairement appelés dans cet ordre.

Le registre par défaut appelant la convention passe également des arguments de gauche à droite, mais les trois premiers arguments qui correspondent sont passés dans les registres. Les registres utilisés pour transmettre des arguments, cependant, sont aussi les registres les plus couramment utilisés pour évaluer les expressions intermédiaires. Le résultat de DB.First = 0 devait être adoptée dans le registre EAX, mais le compilateur a également besoin de ce registre pour appeler ReturnFieldI et pour appeler First. Il était sans doute un peu plus pratique pour évaluer la deuxième fonction première, comme ceci:

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

Une autre chose à remarquer est que votre premier argument est une expression composée. Il y a un appel de fonction et une comparaison. Il n'y a rien à garantir que ces deux parties sont réalisées consécutivement. Le compilateur peut obtenir la fonction appelle de la première manière en appelant First et ReturnFieldI, et comparer ensuite la valeur de retour de First contre zéro.

Autres conseils

Le convention d'appel affecte la façon dont ils sont évalués.
Il n'y a pas un compilateur définir pour contrôler cela.

Pascal est la convention d'appel que vous devez utiliser pour obtenir ce comportement.

Bien que je dépendraient personnellement jamais ce type de comportement.

Le programme d'exemple suivant montre comment cela fonctionne.

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.

Cela vous obliger à écrire vos propres fonctions de IfThen.

Si vous voulez vraiment que ce soit une seule ligne, vous pouvez vraiment faire à Delphes. Je pense qu'il est moche.

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

Pouvez-vous pas changer votre requête d'avoir un seul résultat il faut donc éviter de faire la commande « d'abord »? Tout comme:

SELECT TOP 1 awesomedata1 from awesometable 

Dans Access ...

AFAIK il n'y a pas de directive du compilateur pour contrôler cela. À moins que vous utilisez le stdcall / cdecl / safecall conventions, les paramètres sont transmis de gauche à droite sur la pile, mais parce que la convention de registre par défaut peut passer des paramètres dans les registres ainsi, il pourrait arriver qu'un paramètre est calculé après une vente dans un registre juste avant l'appel. Et parce que l'ordre de registre est fixé (EAX, EDX, ECX) pour les paramètres qui sont admissibles, les registres peuvent être chargés dans un ordre quelconque. Vous pouvez essayer de forcer une « pascals » convention d'appel (vous auriez besoin de réécrire la fonction, de toute façon), mais à mon humble avis est toujours dangereux de se fier à ce genre de code, si le compilateur ne peut pas garantir explicitement l'ordre d'évaluation. Et imposer un ordre d'évaluation peut réduire considérablement le nombre d'optimisations disponibles.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top