Przeciążanie metody OM SET()
From MorphOS Library
Grzegorz Kraszewski
Ten artykuł w innych językach: angielski
Metoda OM_SET() jako strukturę parametrów otrzymuje strukturę opSet. Jest ona zdefiniowana w pliku nagłówkowym <intuition/classusr.h>.
struct opSet { ULONG MethodID; /* zawsze OM_SET (0x103) */ struct TagItem *ops_AttrList; struct GadgetInfo *ops_GInfo; };
Najważniejszym polem struktury jest ops_AttrList. Mieści ono w sobie wskaźnik do taglisty zawierającej atrybuty i ich wartości do ustawienia w obiekcie. Pole ops_GInfo jest historyczną pozostałością i nie jest używane przez nowoczesne elementy systemu takie jak MUI, czy Reggae. Implementacja metody OM_SET() powinna przejrzeć po kolei całą taglistę i ustawić wszystkie rozpoznane atrybuty. Operacja ustawienia atrybutu może się sprowadzać do ustawienia jakiejś wartości w danych obiektu, może też uruchamiać jakieś akcje (na przykład odrysowanie obiektu). Zaleca się jednak, aby bardziej złożone akcje implementować raczej jako metody niż zmiany atrybutu. Wzorcowa metoda OM_SET() może wyglądać następująco:
IPTR MyClassSet(Class *cl, Object *obj, struct opSet *msg) { struct TagItem *tag, *tagptr; IPTR tagcount = 0; tagptr = msg->ops_AttrList; while ((tag = NextTagItem(&tagptr)) != NULL) { switch (tag->ti_Tag) { case SOME_TAG: /* kod zmieniający atrybut SOME_TAG */ tagcount++; break; /* inne atrybuty */ } } tagcount += DoSuperMethodA(cl, obj, (Msg)msg); return tagcount; }
Taglista jest iterowana za pomocą funkcji NextTagItem() z utility.library. Przy każdym wywołaniu funkcja zwraca wskaźnik na kolejny element taglisty. Aktualna pozycja pamiętana jest w zmiennej tagptr. Zaletą użycia tej funkcji jest automatyczna obsługa tagów specjalnych (TAG_MORE, TAG_IGNORE, TAG_SKIP), nie są one zwracane jako kolejne elementy, za to wykonywane są odpowiadające im operacje.
Metoda OM_SET() powinna jako wynik zwracać łączną ilość rozpoznanych i ustawionych atrybutów. Ich zliczanie odbywa się w zmiennej tagcounter. Zmienna jest zwiększana o 1 przy każdym rozpoznanym tagu, a następnie powiększana o ilość atrybutów rozpoznanych w klasach nadrzędnych.
Typowe błędy w implementacji OM_SET() to:
- zignorowanie zliczania rozpoznanych atrybutów,
- wywoływanie metody w klasie nadrzędnej w przypadku default wyrażenia switch. Powoduje to wywołanie metody w klasie nadrzędniej tyle razy ile jest w tagliście atrybutów nierozpoznanych przez klasę (zamiast raz).
W rzadkich przypadkach klasa pochodna może chcieć całkowicie przejąć jakiś atrybut, tak, aby nie był wysłany do klas nadrzędnych. Można to zrobić zastępując identyfikator atrybutu w tagliście przez TAG_IGNORE. Jest tu jednak pewien haczyk. Najczęściej w C i C++ taglista tworzona jest dynamicznie na stosie procesu z argumentów funkcji (np. SetAttrs()). Jest jednak możliwe, że taglista będzie obiektem statycznym (na przykład globalnym, albo stworzonym w zaalokowanym bloku pamięci). W tym przypadku zmiana taga na TAG_IGNORE będzie operacją trwałą, co może skutkować nieoczekiwanymi efektami. Uwaga ta dotyczy również zmiany wartości atrybutu, przed przekazaniem taglisty klasie nadrzędnej. Bezpiecznym rozwiązaniem jest sklonowanie taglisty funkcją CloneTagItems() z utility.library. Następnie dokonuje się zmian w otrzymanej kopii i kopię tę przekazuje klasie nadrzędnej. Po powrocie z DoSuperMethodA() kopię zwalnia się wywołując funkcję FreeTagItems(). Wadą tego rozwiązania jest możliwość wystąpienia błędu przy klonowaniu taglisty z powodu braku pamięci. Możliwość tę trzeba jakoś obsłużyć w kodzie.