Delphi XE a problém s generiky

vložil Igor Gottwald 2. února 2012 20:46

Dnes bych se s našimi čtenáři rád podělil o jednu nepříjemnou zkušenost z Delphi XE, související s použitím generických struktur a tříd.

Dobrá zpráva je, že v Delphi XE2 Update 3 se tato chyba již neprojevuje. Problém se týká případů, kdy nadefinujete generickou strukturu s více jak dvěma typy (TMyStruct<A,B> = record … end), velikost implementace není větší než 4 byty a voláte funkci, která má tuto strukturu vrátit jako svůj výsledek.

Zní to docela složitě, ale příklad to ozřejmí:

    1type
    2  TMyStruct<Type1,Type2> = record
    3    Value1: Type1;
    4    Value2: Type2;
    5  end;
    6
    7  TTestClass<Type1,Type2> = class
    8  public
    9    function Get(AValue1: Type1; AValue2: Type2): TMyStruct<Type1,Type2>;
   10  end;
   11
   12function TTestClass<Type1,Type2>.Get(AValue1: Type1; AValue2: Type2):
   13   TMyStruct<Type1,Type2>;
   14begin
   15  // zde nastane AV chyba
   16  Result.Value1 := AValue1;
   17  Result.Value2 := AValue2;
   18end;
   19
   20function Test(A, B: Byte): Integer;
   21var
   22  ATestClass: TTestClass<Byte,Byte>;
   23  Value: TMyStruct<Byte,Byte>;
   24begin
   25  ATestClass := TTestClass<Byte,Byte>.Create;
   26  try
   27    // tak tohle se nepovede…
   28    Result := ATestClass.Get(A, B).Value1 + ATestClass.Get(A, B).Value2;
   29  finally
   30    ATestClass.Free;
   31  end;
   32end;

Zavoláním funkce TEST s libovolnými parametry zajistíte spolehlivé spuštění výjimky z důvodu porušení ochrany paměti.

Co se vlastně stane? Pokud se podíváte do zdrojového kódu vytvořeného kompilátorem, zjistíte, že metoda Get ukládá výsledek do paměti na předem alokované místo, které musí připravit volající metoda. Naproti tomu ale funkce Test, jelikož se výsledek volání vejde do 32 bitového registru EAX, očekává vrácení výsledku v registru a žádné místo v paměti nepřipraví. Důsledkem toho dojde k přepisu prakticky náhodného místa v operační paměti počítače. Naštěstí je to většinou 0x00000000 a pokud používáte procesor s kontrolou ochrany paměti, tak aplikace skončí AV chybou. V horším případě, který lze rovněž simulovat, ale dojde k přepisu libovolného jiného místa a důsledky mohou být nepředvídatelné.

Jistě si říkáte, že výše popsaný případ je velmi specifický. Nicméně má nepříjemný důsledek a tím je problematické použití generické třídy TDictionary<TKey,TValue>, která je jinak velmi užitečná. Pokud použijete pro TKey a TValue datové typy, jejichž velikost se vejde do 4 bytů, pak není možné použít konstrukci for APair in AnInstanceOfTheDictionary do … , jelikož vrácený datový typ TPair<TKey,TValue> odpovídá výše popsanému typu TMyStruct<Type1,Type2> a první průchod cyklem okamžitě vyvolá AV chybu nebo přepis paměti.

Při použití této konstrukce je tedy nutné mít na zřeteli, aby součet velikostí použitých datových typů byl alespoň 5 bytů anebo změnit cyklus tak, aby procházel klíče slovníku, tj. for AKey in AnInstanceOfTheDictionary.Keys do

Problematické jsou například implementace TDictionary<Byte,Byte>, TDictionary<Byte,Word> apod.

Naopak bez problémů jsou TDictionary<Byte,Integer>, TDictionary<string,Byte> apod.

Věřím, že příznivce Delphi tato ukázka neodradí od používání jinak velice výkonných generických tříd a případně pomůže těm z Vás, kteří se do podobné situace dostali a nedokázali přijít na to, proč jim aplikace padá nebo se nechová dle očekávání.

Igor Gottwald

Pozn. editora: rádi uveřejníme i Vaše příspěvky - nebojte se mi napsat.


Nabízíme Delphi školení na různá témata, primárně ve Vaší firmě.

Tagy: ,

Praxe

Komentování ukončeno

Naše nabídka

Partial English version.

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Anketa

Poslední komentáře

Comment RSS