Порядок вычисления выражений обычно undefined . (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 дает вам желаемый порядок оценки (слева направо), то они оцениваются в обратном порядке того, с которым они были переданы.
Соглашение о вызовах pascal передает аргументы слева направо в стеке. Это означает, что крайний левый аргумент - это тот, что находится внизу стека, а самый правый аргумент - вверху, чуть ниже адреса возврата. Если функция IfThen
использовала это соглашение о вызовах, компилятор может достичь этого макета стека несколькими способами:
То, как вы ожидаете, что каждый аргумент вычисляется и передается немедленно:
push (DB.First = 0)
push DB.ReturnFieldI('awesomedata1')
call IfThen
Оцените аргументы справа налево и сохраните результаты во временных значениях, пока они не будут выдвинуты:
tmp1 := DB.ReturnFieldI('awesomedata1')
tmp2 := (DB.First = 0)
push tmp2
push tmp1
call IfThen
Сначала выделите пространство стека и оцените в любом удобном порядке:
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
с нулем.