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.