توجيه برنامج التحويل البرمجي Delphi لتقييم الحجج في الاتجاه المعاكس

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

  •  27-09-2019
  •  | 
  •  

سؤال

لقد تأثرت حقًا بهذا Delphi two بطانة باستخدام وظيفة 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 إذا كان لديه شيء فيه ، ربما لم يكن من المنطقي خلاف ذلك)

من الواضح أن هذه ليست مشكلة كبيرة ، حيث يمكنني جعلها تعمل مع خمس بطانات قوية. لكن كل ما أحتاجه من أجل العمل هو بالنسبة لدلفي لتقييم DB.First أولاً و DB.RETURNFIELDI الثانية. لا أرغب في تغيير الرياضيات ، ولا أعتقد أن هذا يذكرني بصنع إيفثين بسبب وجود 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.

سيتطلب منك ذلك كتابة وظائف ifthen الخاصة بك.

إذا كنت تريد حقًا أن يكون هذا بطانة واحدة ، يمكنك فعل ذلك في دلفي. أعتقد أنه يبدو قبيحًا.

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

ألا يمكنك تغيير استعلامك للحصول على نتيجة واحدة فقط ، لذا تجنب القيام بالأمر "الأول"؟ تماما مثل :

SELECT TOP 1 awesomedata1 from awesometable 

في الوصول ...

AFAIK لا يوجد توجيه مترجم للسيطرة على هذا. ما لم تكن تستخدم اتفاقيات STDCALL/CDECL/SAFECALL ، يتم تمرير المعلمات من اليسار إلى اليمين على المكدس ، ولكن نظرًا لأن اتفاقية السجل الافتراضية يمكن أن تمرر المعلمات في السجلات أيضًا ، فقد يحدث ذلك في حساب المعلمة لاحقًا في السجل قبل المكالمة مباشرة. ولأن أمر التسجيل فقط يتم إصلاحه (EAX ، EDX ، ECX) للمعلمات المؤهلة ، يمكن تحميل السجلات بأي ترتيب. يمكنك محاولة إجبار اتفاقية الاتصال "Pascal" (ستحتاج إلى إعادة كتابة الوظيفة ، على أي حال) ولكن IMHO دائمًا ما يكون خطراً على الاعتماد على هذا النوع من الكود ، إذا لم يتمكن المترجم بشكل صريح من ترتيب التقييم. وفرض أمر تقييم قد يقلل بشكل كبير من عدد التحسينات المتاحة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top