Difference between revisions of "Przeciążanie metody OM SET()"

From MorphOS Library

(Translation in progress.)
(Translation finished.)
 
(2 intermediate revisions by the same user not shown)
Line 13: Line 13:
 
  };
 
  };
  
The most important field is ''ops_AttrList''. It is a pointer to a [[Taglists|taglist]] containing attributes and values to be set. The ''ops_GInfo'' field is an obsolete legacy thing and is not used by modern components like MUI or Reggae. The method implementation should iterate the taglist and set all attributes recognized. The operation of setting an attribute may be just setting some field in an object instance data, it may also trigger some actions (like for example object redrawing). It is recommended however that complex actions are implemented as methods rather than attribute changes. A reference implementation of ''OM_SET()'' may look like this:
+
Najważniejszym polem struktury jest ''ops_AttrList''. Mieści ono w sobie wskaźnik do [[Taglists|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)
 
  IPTR MyClassSet(Class *cl, Object *obj, struct opSet *msg)
Line 27: Line 27:
 
     {
 
     {
 
       case SOME_TAG:
 
       case SOME_TAG:
         /* attribute setting actions for SOME_TAG */
+
         /* kod zmieniający atrybut SOME_TAG */
 
         tagcount++;
 
         tagcount++;
 
       break;
 
       break;
 
   
 
   
       /* more tags here */
+
       /* inne atrybuty */
 
     }
 
     }
 
   }
 
   }
Line 39: Line 39:
 
  }
 
  }
  
The taglist iteration is done with the ''NextTagItem()'' function from the utility.library. The function returns a pointer to the next tag each time it is called and keeps the current position in ''tagptr''. The advantage of this function is automatic handling of special tag values (''TAG_MORE'', ''TAG_IGNORE'', ''TAG_SKIP''), they are not returned, but their actions are performed instead.
+
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.
  
The ''OM_SET()'' function returns the total number of recognized tags. It is implemented with ''tagcounter''. It gets incremented on every tag recognized and finally the number of tags recognized by superclass(es) is added.
+
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.
  
Common bugs in ''OM_SET()'' implementation are: ignoring tag counting and calling the super method in the ''default'' case of a ''switch'' statement. The second bug causes the supermethod to be called multiple times, once for every tag not handled by the subclass.
+
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).
  
  
 
----
 
----
<small>In some rare cases a subclass may want to override an attribute completely, so it is not passed to superclasses. This can be done by replacing the tag (not value!) by ''TAG_IGNORE''. There is one caveat however. In most cases in C and C++, the taglist is built dynamically on the stack from variable arguments of a function like ''SetAttrs()''. It is possible however, that a taglist is a static object (for example a global one, or created in an allocated chunk of free memory). In this case changing a tag is a '''permanent''' operation, which may have unexpected results. This remark also applies for changing a value of a tag before passing it to a superclass. A safe solution is to clone the taglist with the ''CloneTagItems()'' function from the ''utility.library''. Then changes are made in the copy and this copy is passed to the superclass. The copy is then freed with ''FreeTagItems()''. The disadvantage of this solution is that cloning a taglist may fail due to lack of free memory and this possibility must be handled somehow.</small>
+
<small>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.</small>

Latest revision as of 08:44, 26 January 2011

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.