Quantcast
Channel: Planet Object Pascal
Viewing all articles
Browse latest Browse all 1725

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

$
0
0

ב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 ומגלה שהן זהות.
אח"כ אני מדפיס גם את תוכן המחרוזת לוודא כי הן זהות.

וזה כל הסיפור


Filed under: FPC, Object Pascal, טכנולוגיה, פיתוח, קוד פתוח, תוכנה, תכנות Tagged: fgl, fpc, generics, key value

Viewing all articles
Browse latest Browse all 1725

Trending Articles