הרצת מתודות בצורה דינאמית חלק ראשון

לאחרונה יצא לי לשנות גישה במספר מערכות שאני בונה, ובהם בrpc לאפשר להריץ מתודות פיזיות שיש ברשותי (לאחר שזה עבר כמובן אישור של white list כלשהו).

אז החלטתי לחקור יותר לעומק את הנושא במגוון שפות שאני מפתח בהן, ולא רק בשפת רובי (אשר תהיה הבאה בתור – בחלק הבא), ובעצם להציג הרבה הבנות ותובנות שאפשר להגיד על כל שפה ושפה.

אתחיל בשפת פסקל מונחת עצמים.
הדרך לעשות את זה בשפה הזו, היא על ידי שימוש קל ב RTTI. לשם כל אנחנו חייבים לגרום למתודה להיות גלויה לכך, ולכן היא חייבת להיות בחלק ה published, שזה למעשה התפקיד שלו:

...
type
  TMyClass = class
    procedure SayHi;
  end;
...

כאשר לא מציינים שום דבר לפני הצהרת המתודות ממש בהתחלת המחלקה, המתודות שייכות לאיזור ה published (בניגוד ל ג'אווה, PHP ו ++C, שם מצהירים על כל מתודה בנפרד). במידה וזה בעייתי במעט, אז אפשר ליצור את איזור ה published בצורה מוצהרת שתחתיו יהיו המתודות שנרצה, כלומר:

...
type
  TMyClass = class
  published
    procedure SayHi;
  end;
...

והשגנו את אותו האפקט.

היות ואנחנו רוצים להגיד למהדר ליצור לנו הוראות RTTI בפועל, אנחנו גם נספק את הפקודה למהדר לעשות זאת:

...{$M+}...

אחרת המהדר לא יצור לנו מידע שכזה בזמן ריצה.

עידכון: החלק של הרצת המתודה בזמן ריצה, תכתב בצורה הבאה:

...
function ExecMethod(Instance : TObject; Name : String) : Boolean;
type TProc = procedure of object;
var
  Method : TMethod;
  Exec   : TProc;
begin
  Method.Data := Pointer(Instance);
  Method.Code := Instance.MethodAddress(Name);
  Exec        := TProc(Method);
  Result      := Assigned(Exec);
  if Result then Exec;
end;

...

עדכון: הקוד שומר בתוכו גם את ה instance וגם את הכתובת ריצה של המתודה. לאחר מכן, במידה והמתודה נמצאה, נריץ אותה, אחרת נחזיר ערך שלילי.

השימוש בפונקציה יתבצע בצורה הבאה:

...
if not ExecMethod(MyClass, 'sayhi') then
   writeln(STDERR, 'Could not find SayHi method on MyClass instance');
...

כאשר MyClass הוא בעצם Instance של TMyClass אשר יצרנו קודם. חשוב לזכור כי בפסקל אין הבדל בין אות גדולה לקטנה בשמות, ולכן זה שכתבתי את שם המתודה באותיות קטנות בלבד, אינו משנה לכך אם הוא יצליח למצוא אותה או שלא.

הקוד המלא:

רשומה זו פורסמה בקטגוריה Delphi, FPC, Object Pascal, טיפים וטריקים, טכנולוגיה, פיתוח, קוד פתוח, תוכנה, תכנות, עם התגים , . אפשר להגיע ישירות לרשומה זו עם קישור ישיר.

6 תגובות על הרצת מתודות בצורה דינאמית חלק ראשון

  1. יוחאי הגיב:

    זה בהחלט עדיף על להעביר מצביע כללי, שמצביע למתודה.
    ואם לתת לקוד לדבר ובתקווה שארבע שעות שינה לא יפגעו בבהירות שלו, כן אני יודע שאני מכניס קיצורים:
    var
    pMethod: ^TProcedure of TObject;
    pJustAPointer:pointer;
    begin

    pMethod := @MethodName;
    pJustAPointer := @pMethod;

    בצד שני:
    pMethod := ^pJustAPointer;
    pMethod^;

    • ik_5 הגיב:

      תיקנתי את הקוד להיות קצת יותר נכון, בגישה שדומה לשלך

      • יוחאי הגיב:

        גרמת לי להסתכל על ההגדרה של TMethod.
        אני רק בשלבי למידה של השפה ביחד עם מימוש פרוייקט שעלה לי לראש, זה גרם לי לחשוב.
        האם:
        1. אפשר ככה להוסיף לאירוע של מחלקה מתודות בזמן ריצה ?
        2. אפשר ככה לשנות את צורת הפעולה של מתודה של מחלקה באירוע מסוים בזמן ריצה ?

        • ik_5 הגיב:

          1.
          type TMyCallBack = procedure(Sender : TObject; MethodName : String) of object;
          אם הבנתי את הכוונה שלך נכון

          2. אני בכלל לא חושב שהבנתי את השאלה

  2. יוחאי הגיב:

    1. ליצור מופע של מחלקה, להגדיר מתודה ריקה ואז:
    Instance.EmptyMethod.Data := …

    2.
    Instance.Method.Code := …
    כדי לשנות את פעולת המתודה.

    תהיה לי אפשרות רק מאוחר יותר לכתוב קוד ולנסות, אבל אני ממש סקרן :)

  3. פינגבק: הרצת מתודות בצורה דינאמית חלק שני | לראות שונה

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s