הדגמה מתקדמת לשימוש ב TFPSMap

בFPC 2.2.0 נוספה ספריה בשם fgl בייחד עם התוספת לשימוש ב Generics.

הספרייה מאפשרת לקבל שני מימושים של מחלקות, האחת עובדת בגישה הרגילה של שימוש במצביעים ו/או TObject לבצע פעולות, והשניה לעבוד עם generics.

לאחרונה ניסיתי לעשות שימוש במחלקה בשם TFPSMap כאשר אני מנסה להשתמש ב key ו value של מחלקות. אבל גיליתי בעיה – הוא מאבד את האיבר ולמעשה הצורה שהשתמשתי בה גרמה לדליפת זיכרון. מצד אחד הזיכרון נכנס לרשימה, אבל מצד שני מעולם לא הצלחתי לתפוס אותו חזרה.

דיווחתי על כך באג, אבל הסתבר שאני פשוט השתמשתי בספרייה לא נכון. הנה הצורה הנכונה לשימוש בTFPSMap:

{$mode objfpc}
uses sysutils, fgl;

type
  TMyKey = class
  public
    Key : String;

    constructor Create;
  end;

  TMyValue = class
  public
    Value : String;

    constructor Create;
  end;

constructor TMyKey.Create;
begin
  Key := 'A Key';
end;

constructor TMyValue.Create;
begin
  Value := 'A Value';
end;

var
  Map    : TFPSMap;
  Key    : TMyKey;
  Value  : TMyValue;
  Value2 : TMyValue;

begin
  Map   := TFPSMap.create(SizeOf(key), SizeOf(Value));
  Key   := TMyKey.create;
  Value := TMyValue.create;

  Value.Value := 'here';
  Map.add(@Key, @Value);

  Value2 := TMyValue(Map.KeyData[@Key]^);
  writeln(format('%P, %P, %S, %S', [Pointer(Value), Pointer(Value2),
                                    Value.Value, Value2.value]));
  Key.free;
  Value.Free;
  Map.free;
end.

בחלקים הראשונים יצרתי סתם מחלקות בשביל ההדגמה, הן לא באמת חשובות להסבר.
בחלק ריצה הרגיל, כבר יש דברים אשר חשובים להסבר.

Map   := TFPSMap.create(SizeOf(key), SizeOf(Value));

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

 Map.add(@Key, @Value);

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

בשביל לחלץ את המידע, השתמשתי בקוד הבא:

 Value2 := TMyValue(Map.KeyData[@Key]^);

כאן אני מחזיר חזרה את הערך של ה instance מכתובת הזיכרון שלו אל תוך משתנה חדש.
ה casting מתבצע בגלל ה strong type. אני מכריח את הקומפיילר בעצם להבין כיצד להתנג נכון עם התוכן, אחרת הוא ידווח על שגיאה כי מה שסיפקתי ומה שהוא מצפה הם שני טיפוסים שונים.

ובשביל להיות בטוח אני מדפיס על המסך את המידע:

 writeln(format('%P, %P, %S, %S', [Pointer(Value), Pointer(Value2),
                                   Value.Value, Value2.value])); 

בהתחלה אני מדפיס את 2 כתובות הזיכרון של ה instance ומגלה שהן זהות.
אח"כ אני מדפיס גם את תוכן המחרוזת לוודא כי הן זהות.

וזה כל הסיפור

להשאיר תגובה

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

הלוגו של WordPress.com

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

תמונת Twitter

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

תמונת Facebook

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

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

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

מתחבר ל-%s