Obiekty generyczne - mała ciekawostka

0

Podzielę się z wami ciekawostką, moim odkryciem na temat obiektów. Być może czasami też używacie starego modelu obiektowego. Osobiście należy do tych leniuchów, którzy uważają, że definiowanie klas do struktur manipulujących innymi strukturami jest niepotrzebną ornamentyką. Uprzedzając ewentualną krytykę, sprawdziłem na forum Lazarusa, że jest więcej takich leniuszków. Nawiasem mówiąc zabawne, że w Delphi najpierw usunięto stary model obiektowy, ale potem nagle uznano, że są potrzebne zaawansowane rekordy. Na forum Lazarusa twierdzą, że wstydzili się przyznać, że decyzja z obiektami była pochopna.
Wracając jednak do Free Pascala stary tryb obiektowy jest słabo udokumentowany (wiecie np., że właściwości działają, chociaż nigdzie nie ma nic na ten temat?). Coś zacząłem poprawiać w kodzie i zrobiłem się nagle ciekawy czy ten model jest dalej rozwijany.
I tu kompletna niespodzianka (przynajmniej dla mnie). Jest. Otóż możliwe są generyczne obiekty. No dobra, to postanowiłem iść na całość.
A metody generyczne? I wiecie co? Kompiluje się w FPC 3.2.2…

program TestGeneric1;

{$mode objfpc}

type
  TSuma1 = object
      x: integer;
      generic function Suma<T> (a,b: T): T;
  end;

  generic function TSuma1.Suma<T>(a, b: T): T;
  begin
    Result := a + b;
  end;

var
  Suma1:TSuma1;

begin
   writeln(Suma1.Specialize Suma<string>('Metody generyczne?', ' TAK!!'));
   readln;
end.  

Na koniec dodam, że to jest tak nieudokumentowane, że JEDI formater głupieje… Ale mnie się właśnie przydało w moim programie. Może więc jeszcze komuś się przyda. :)

1

Ale generic jest od Free Pascala 2.2... Więc żadnej ameryki nie odkryłeś.

0
Riddle napisał(a):

Ale generic jest od Free Pascala 2.2... Więc żadnej ameryki nie odkryłeś.

Troszkę chyba nie tak:

  1. Nieprawda. Funkcje generyczne (a więc metody też) są od wersji 3.2, a nie od 2.2.
  2. Nie doczytałeś. Ten post jest o starym modelu obiektowym. A o tym w Release Notes to raczej nie ma. :)
  3. Post nie miał odkryć ameryki. Taka ciekawostka.
0

https://www.freepascal.org/docs-html/ref/refse52.html#x106-1300008.1

As of version 2.2, Free Pascal also officially has support for templates or Generics.

0

Nie chce się kłócić, ale nie:
https://wiki.freepascal.org/FPC_New_Features_3.2.0
Nie było tego w 2.2. Tam mogłeś mieć klasę generyczną, a nie funkcje.

Ale mniejsza z tym, chodziło mi o obiekty . :)

2
KKuba napisał(a):

Być może czasami też używacie starego modelu obiektowego.

W obecnym projekcie używam, bo dają mi to samo co zaawansowane rekordy, ale dodatkowo można je dziedziczyć bez żadnych kombinacji z ich zawartością.

Osobiście należy do tych leniuchów, którzy uważają, że definiowanie klas do struktur manipulujących innymi strukturami jest niepotrzebną ornamentyką.

Jeśli tak uważają, to są w błędzie. Struktury, obiekty, klasy i interfejsy to nie jest ”ornamentyka”, a struktury danych charakteryzujące się konkretną funkcjonalnością. Gdyby było inaczej, to mielibyśmy do czynienia z cukrem składniowym, a nie mamy.

Nawiasem mówiąc zabawne, że w Delphi najpierw usunięto stary model obiektowy, ale potem nagle uznano, że są potrzebne zaawansowane rekordy.

Nie wiem na ile jest to prawda, ale zaawansowane rekordy nie są zamiennikiem klasycznego modelu obiektowego. Różnica między rekordami a obiektami jest taka, że obiekty wspierają dziedziczenie oraz VMT (opcjonalnie). Ale z drugiej strony, obiekty mogą być kompatybilne z najprostszymi rekordami — wystarczy nie inicjalizować VMT i voilà. Dziedziczenie nadal będzie zapewnione (na poziomie języka), ale rozmiar obiektu (na podstawie sumy rozmiaru jego pól) będzie identyczny jak w przypadku struktur.

Benefitem korzystania z obiektów zamiast z rekordów jest również to, że język zapewnia wbudowaną obsługę enkapsulacji zawartości, zamiast wymagać jej aktywacji za pomocą dyrektywy {$MODESWITCH ADVANCEDRECORDS}.

Na forum Lazarusa twierdzą, że wstydzili się przyznać, że decyzja z obiektami była pochopna.

Znów, nie wiem na ile w tym prawdy, ale jeśli tak było, to zdecydowanie uznaję taką decyzję za pochopną. Pascal dorobił się bogactwa najróżniejszych struktur danych, których w innych językach nie ma. Mamy ich aż sześć:

  • rekordy proste,
  • unie, nazywane variant records (których składnia to jakiś smutny żart),
  • rekordy zaawansowane,
  • obiekty (klasyczny model OOP),
  • klasy (współczesny model OOP),
  • interfejsy.

Każda struktura danych w powyższej liście charakteryzuje się odmienną funkcjonalnością, w kolejności od najmniejszej do największej (interfejsów nie liczę). A skoro tak, to rezygnowanie z którejkolwiek byłoby czystą głupotą.

Zwykłe rekordy to najprostsze struktury przeznaczone wyłącznie do przechowywania danych, unie rozszerzają ich funkcjonalność o możliwość deklaracji pól pod jednym adresem (coś jak absolute). Rekordy zaawansowane dodatkowo wspierają prostą hermetyzację (tylko private i public) i mogą zawierać metody. Co jednak istotne, wszelkie metody zadeklarowane wewnątrz typu rekordu nie są jego częścią i oznacza to, że rekord zawierający same metody de facto ma rozmiar 0 bajtów.

I tu wkraczają klasyczne obiekty. Te wspierają wszystko to co powyższe struktury, ale dodatkowo wspierają (mono)dziedziczenie oraz mogą (acz nie muszą) posiadać VMT. Jeśli programista potrzebuje najprostszych struktur danych, które mogą być dziedziczone i hermetyzowane (wszystkie sekcje oprócz published), to powinien wybrać obiekty, bo są/mogą być zgodne pamięciowo z rekordami, ale dodatkowo ułatwiać pisanie kodu (wbudowane dziedziczenie i brak zabawy z dyrektywami). Poza tym, w odróżnieniu od klas, obiekty domyślnie alokowane są na stosie (ale mogą też być alokowane na stercie).

Klasy natomiast to najbardziej zaawansowane struktury danych, jakie natywnie wspierane są przez język. Wbudowana obsługa dziedziczenia z innych klas, wielodziedziczenia interfejsów, wbudowane VMT, pełna funkcjonalność hermetyzacji, RTTI itd. itd. Jest to najczęściej wykorzystywany model reprezentacji danych w typowych projektach, bo najbardziej zaawansowany, elastyczny i prosty w użyciu.

Wracając jednak do Free Pascala stary tryb obiektowy jest słabo udokumentowany (wiecie np., że właściwości działają, chociaż nigdzie nie ma nic na ten temat?).

Dokumentacja jest i ma się dobrze — Free Pascal, Chapter 5 (Objects). Są też materiały w wiki Free Pascala na ten temat.

Coś zacząłem poprawiać w kodzie i zrobiłem się nagle ciekawy czy ten model jest dalej rozwijany.

Chyba jest, a przynajmniej obecna forma jest nieco bardziej funkcjonalna niż ta np. z Turbo Pascala.

Dla przykładu, jest wsparcie dla sekcji strict private i strict protected, co nie jest udokumentowane i czego nadal nie rozumie completion box, bo listuje ich zawartość tam, gdzie nie ma do nich dostępu i gdzie kompilator zgłosi błąd identifier idents no member. ;)

A metody generyczne? I wiecie co? Kompiluje się w FPC 3.2.2…

Nie ma powodów, aby się nie kompilowały — składnia kodu jest jak najbardziej prawidłowa. Jedyne co kłuje w oczy to ten cholerny specialize, ale tak zostały pierwotnie zaprojektowane generyki we Free Pascalu. Ale zawsze można włączyć automatyczną specjalizację, za pomocą dyrektywy {$MODESWITCH IMPLICITFUNCTIONSPECIALIZATION}, choć w oficjalnej wersji FPC/Lazarusa, ten przełącznik jeszcze nie jest wspierany.

0

W sumie to odpisze, mimo że się zasadniczo zgadzam z tym co ładnie napisałeś, z drobnymi uzupełnianiami:

  1. Coś dziwnego się podziało bo mój post dotyczył implementacji generyków w modelu obiektowym, a dyskusja poszła w jakimś innym kierunku. Chyba zły tytuł dałem :(
  2. W pełni się zgadzam, ze fajne jest to, że w FPC jest kilka rożnych podobnych konstrukcji, przydających się w różnych miejscach. Ale wyraźnie napisałem, że osobiście używam obiektów z powodu ich alokowania na stosie. Czyli to samo co piszesz, więc nie za bardzo wiem czemu mnie krytykujesz. :) „Ornamentyka” dotyczyła wyłącznie mojego prywatnego poglądu, że pewnych przypadkach używanie klas jest zbędne. I tylko tyle. Strasznie się czepiacie słówek – mimo że merytorycznie napisane było coś innego.
  3. Masz rację z zaawansowanymi rekordami, z tym, że twórcy FPC uznali, że to jest jednak tak podobne to modelu obiektowego, że domyślnie jest wyłączone, a obiekty są włączone (przynajmniej obecnie).
  4. Dokumentacja obiektów wcale nie ma się dobrze. O wszystkich rzeczach, o których mówimy czyli o generykach i właściwościach w niej nie ma. To co jest w tym poście jest wynikiem naszych eksperymentów.

Podsumowując chyba dobrze, że powstał ten post. Bo właśnie pokazujemy mniej znane konstrukcje języka, czyli jest użyteczny, a temat obiektów jest bardzo rzadko poruszany.

0

Nie odbieraj mojego postu jako krytyki — to co opisałem w nim ma na celu ilustrować to jak wygląda sprawa z klasycznymi obiektami i że nadal są one przydatne. Napiszę nawet więcej — obiekty powinny być jedyną wspieraną strukturą danych, nieco bardziej zaawansowaną niż rekordy, ale mniej niż klasy. To zaawansowane rekordy są wybrykiem natury i to one nigdy nie powinny trafić do standardu języka.

  1. Masz rację z zaawansowanymi rekordami, z tym, że twórcy FPC uznali, że to jest jednak tak podobne to modelu obiektowego, że domyślnie jest wyłączone, a obiekty są włączone (przynajmniej obecnie).

Jeśli tak było, to twórcy dobrą decyzję podjęli. Choć tak szczerze pisząc, automatycznie włączone zaawansowane rekordy w niczym by nie przeszkadzały, nie powodowałyby problemów ze wsteczną kompatybilnoscią.

4, Dokumentacja obiektów wcale nie ma się dobrze. O wszystkich rzeczach, o których mówimy czyli o generykach i właściwościach w niej nie ma.

Bo szukasz nie tam gdzie trzeba — szukaj w dokumentacji generyków, bo obe mają zastosowanie wszędzie, a dodatkowo, ich możliwości są takie same w przypadku obiektów, jak i w przypadku klas. Obiekty również mogą być generyczne i posiadać generyczne metody (rekordy w sumie też). Generyki to całkiem osobna gałąź funkcjonalności, dlatego jest opisana w innym miejscu dokumentacji.

0

Na szczęście sprawa nam się ograniczyła do jednego punktu dyskusji. Moim zdaniem jeśli w dokumentacji klas coś jest, a w dokumentacji obiektów tego czegoś nie ma (np. właściwości), mimo że to coś jednak istnieje, to jest to zwyczajnie luka. I nie jest przy tym ważne, że gdzieś indziej coś pisze, bo mówimy o tym rodzaju dokumentacji, a nie innym rodzaju. Pozwolę się więc nie zgodzić z Tobą w tym jednym aspekcie. Być może dlatego, że na końcu jestem prawnikiem i tak mnie logiki kiedyś na studiach nauczyli. Ale tak jak wspominałem w reszcie się już chyba idealnie zgadzamy. :)

0

Pomyśl o tym inaczej — generyki są całkiem odrębną gałęzią funkcjonalności i dotyczą nie tylko metod (metod klas, obiektów, rekordów), ale też zwykłych funkcji oraz typów danych, od klas, poprzez obiekty i rekordy, a na macierzach kończąc. Z tego też powodu nie powinny być opisane w dziale z klasami, a w osobnej kategorii dokumentacji, gdzie byłoby pokazane jak ich używać, w połączeniu z różnymi elementami języka.

Tak faktycznie jest — generyki posiadają swoją kategorię w dokumentacji i dla mnie jest to logiczne. Przykładowych kodów jest trochę, ale nie pokrywają one wszystkich możliwości generyków, co jest sporym minusem. :/

0

To jest klasyka. Oboje mamy rację, zależy jak na to spojrzeć. Nie zmienia to faktu, że dokumentacja obiektów jest niekompletna też w innych miejscach. To jest o tyle kłopot, że ta funkcjonalność wyglądała na "porzuconą" co mnie osobiście zmyliło. A tak nie jest, bo dalej jest przez team rozwijana. I w efekcie teraz kod muszę poprawiać. :( A ten błąd formatera w odniesieniu do kodu obiektów to chyba zgłoszę bo mnie irytuje strasznie, rozwala mi formatowanie modułu.

0
KKuba napisał(a):

A ten błąd formatera w odniesieniu do kodu obiektów to chyba zgłoszę bo mnie irytuje strasznie, rozwala mi formatowanie modułu.

„Ten” błąd, czyli który? Napisz w końcu konkretnie o co chodzi.

0

Ale o co Ci chodzi? Coś nie jasno napisałem? Przecież pisałem, że formater nie rozpoznaje generic w definicji metody obiektu.
Poniższy kod w odróżnieniu do tego na początku działa z formaterem:

program TestNieGeneric1;

{$mode objfpc}

type
  TSuma1 = object
    x: integer;
    function Suma(a, b: string): string;
  end;

  function TSuma1.Suma(a, b: string): string;
  begin
    Result := a + b;
  end;

var
  Suma1: TSuma1;

begin
  writeln(Suma1.Suma('Metody generyczne?', ' NIE!!'));
  readln;
end. 
0
KKuba napisał(a):

Ale o co Ci chodzi? Coś nie jasno napisałem? Przecież pisałem, że formater nie rozpoznaje generic w definicji metody obiektu.

Nigdzie nie napisałeś, że „nie rozpoznaje generic w definicji metody” — sprawdziłem nawet szukajką tekstu w przeglądarce i nigdzie takiego stwierdzenia nie znalazłem. No nic, najwyraźniej starzeję się.

IMO najlepiej by było, gdyby deweloperzy Lazarusa sami stworzyli formatter i go rozwijali zgodnie z rozwojem FPC. Bo z tego co widzę w oknie informacji JEDI Code Formattera, jest to wersja z 2009 roku. Nie wiem czy faktycznie, ale takie informacje są podane.

0

Ok nie zacytowałem się dokładnie. Dokładnie to pisało: "A metody generyczne? I wiecie co? Kompiluje się w FPC 3.2.2… (...) Na koniec dodam, że to jest tak nieudokumentowane, że JEDI formater głupieje…". Wyraziłem się jak widać nie jasno, więc przyjmuję krytykę. A starzeje to się ja... :)

0

raczej powinno być coś takiego:

program Project1;

uses
  Classes;

type

  { TSuma1 }

  generic TSuma1<T> = object
      x: integer;
      function Suma(a,b: T): T;
  end;

  TSuma1String = specialize TSuma1<String>;
  TSuma1Integer = specialize TSuma1<Integer>;
  TSuma1Double = specialize TSuma1<Double>;

{ TSuma1 }

function TSuma1.Suma(a, b: T): T;
begin
  Result := a + b;
end;

var
  s1s: TSuma1String;
  s1i: TSuma1Integer;
  s1d: TSuma1Double;
  ////lub
  //s1s: specialize TSuma1<String>;
  //s1i: specialize TSuma1<Integer>;
  //s1d: specialize TSuma1<Double>;

begin
  writeln(s1s.Suma('Metody generyczne?', ' TAK!!'));
  writeln(s1i.Suma(1, 8));
  writeln(s1d.Suma(2.2, 3.33));
  readln;
end.

i formatter działa bez problemu (również uzupełnianie kodu).
Źródła:
Dokumentacja
Przykład

0

No tak tyle, że nie o tym od wczoraj piszemy. To jest funkcjonalność z wersji 2.x A cały post jest o funkcjonalności z wersji 3.2 czyli: 'Support for Generic Routines'.
BTW, w pewnych przypadkach lepsze jest to co co piszesz, a w innych jest lepsze to co ja pisze. Jak pisał tu wyżej kolega istotne, ze są obie konstrukcje. Ale to druga sprawia problem, o którym dyskutowaliśmy wczoraj.

0

Zawsze można skorzystać z przełącznika {$MODE DELPHI} i mieć zapewnioną automatyczną specjalizację.

1 użytkowników online, w tym zalogowanych: 0, gości: 1