Delphi Compiler Directive для оценки аргументов в обратном порядке

StackOverflow https://stackoverflow.com/questions/3054526

  •  27-09-2019
  •  | 
  •  

Вопрос

Я был действительно впечатлен этим Delphi два лайнера, используя функцию IFTHEN из MATH.PAS. Тем не менее, он сначала оценивает db.returnfieldi, что к сожалению, потому что мне нужно позвонить в DB.First, чтобы получить первую запись.

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

(Как уже бессмысленное разъяснение, потому что уже уже так много хороших ответов. Я забыл упомянуть, что 0 - код, который DB.First возвращает, если он есть что-то в этом, может не иметь смысла иначе)

Очевидно, что это не такое большое дело, так как я мог бы сделать это работать с пятью прочными ланерами. Но все, что мне нужно для этого для работы, предназначена для Delphi для оценки DB.First First и DB.returnfyfififififi. Я не хочу менять math.pas, и я не думаю, что это гарантирует мне перегружен, потому что есть, потому что есть 16 функций IFTHEN.

Просто дайте мне знать, что такое директива компилятора, если есть еще лучший способ сделать это, или если нет способа сделать это и любой процедурой, чья процедура состоит в том, чтобы вызвать DB.First и вслепую извлекать первое, что он находит не настоящий программист.

Это было полезно?

Решение

Оцен оценки выражений обычно неопределенный. Отказ (C и C ++ одинаковы. Java всегда оценивает слева направо.) Компилятор не предлагает контроля над ним. Если вам нужны два выражения, которые необходимо оценивать в определенном порядке, то напишите свой код по-другому. Я бы не беспокоился о количестве строк кода. Линии дешевые; Используйте столько, сколько вам нужно. Если вы часто используете этот шаблон, напишите функцию, которая все это все вверх:

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 получает Значения аргументов в том же порядке во всех трех случаях, но функции не обязательно вызываются в этом порядке.

Конвенция о вызове реестра по умолчанию также передает аргументы слева направо, но первые три аргумента, которые соответствуют, передаются в регистры. Регистры, используемые для пропускания аргументов, однако, также являются регистрами, наиболее часто используемыми для оценки промежуточных выражений. Результат DB.First = 0 необходимо пропустить в регистре EAX, но компилятор также нужен этот регистр для вызова ReturnFieldI и для звонка First. Отказ Вероятно, было немного удобнее оценить вторую функцию первым, как это:

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

Еще одна вещь, чтобы указать, состоит в том, что ваш первый аргумент является выражением соединения. Есть функциональный вызов и сравнение. Там нет ничего, чтобы гарантировать, что эти две части выполняются последовательно. Компилятор может получить вызовы функций сначала, позвонив First а также ReturnFieldI, и после этого сравните 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.

Это потребует от вас, чтобы написать свои собственные функции IFTEN.

Если вы действительно хотите, чтобы это было один вкладыш, вы действительно можете сделать это в Delphi. Я просто думаю, что это выглядит уродливым.

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

Разве вы не можете изменить свой запрос, чтобы иметь только один результат, поэтому избегайте выполнения команды «Первая»? Как :

SELECT TOP 1 awesomedata1 from awesometable 

В доступе ...

AFAIK Нет директивы компилятора для контроля этого. Если вы не используете конвенции STDCALL / CIDECL / SAFECAL, параметры пропускаются влево вправо на стеке, но поскольку Конвенция о регистрации по умолчанию также может передавать параметры в регистров, возможно, это может произойти, что параметр рассчитывается позже, поставленный в реестре непосредственно перед вызовом. И поскольку только заказ регистра фиксируется (EAX, EDX, ECX) для параметров, которые квалифицируются, регистры могут быть загружены в любом порядке. Вы можете попытаться заставить призывную конвенцию «Паскаль» (вам нужно будет переписать функцию, в любом случае), но ИМХО всегда опасен для полагаться на такой код, если компилятор не может четко гарантировать порядок оценки. И внушительный порядок оценки может значительно уменьшить количество доступных оптимизаций.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top