Obligement - L'Amiga au maximum

Mercredi 20 septembre 2017 - 07:37  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : C - programmation objet et classe BOOPSI
(Article écrit par Eric Totel et extrait d'Amiga News - janvier 1996)


Présentation

BOOPSI est l'abréviation de "Basic Object Oriented Programming System for Intuition" c'est-à-dire "Système de programmation orienté objet pour intuition". Il permet entre autres :
  • La création de nouvelles classes privées ou publiques, une classe constituant la description en termes de fonctionnalités et d'attributs des futurs objets.
  • L'accession à la notion d'héritage, une nouvelle classe pouvant hériter des attributs et fonctionnalités d'une classe déjà existante.
De nombreuses classes BOOPSI existent déjà au niveau du système, constituant une arborescence de quatorze classes publiques :

rootclass   <--- Class         <--- modelclass Interconnexion
            <--- imageclass    <--- frameClass Image rectangulaire
                               |--- sysClass Images système
                               +--- fillrectclass Rectangle avec frame et pattern
                               +--- itextClass Affichage de texte
            <--- gadgetclass   <--- propgclass Gadget proportionnel
                               |--- strgclass Gadget chaîne
                               |--- buttongclass <---frbuttonclass Boutons
                               +--- groupgclass Groupement de plusieurs
                                                éléments

Ainsi, buttongclass hérite de gadgetclass, qui elle-même hérite de rootclass. En nomenclature BOOPSI, gadgetclass est une superclasse (classe parent) de buttongclass. Il est à remarquer que la notion d'héritage multiple (grâce à laquelle une classe peut hériter de plusieurs autres à la fois) n'existe pas avec BOOPSI.

2. Les objets BOOPSI

2.a Création d'un objet

Un objet BOOPSI résulte de l'instantiation d'une classe existante publique ou privée. Cette création se réalise par l'intermédiaire des fonctions :

    APTR NewObjectA(Class *privclass, UBYTE *pubclass, struct TagItem *myattrs)
    APTR NewObject (Class *privclass, UBYTE *pubclass, Tag1, value1, ...)

    privclass: utilisé pour l'instantiation d'une classe privée (NULL sinon).
    pubclass : utilisé pour l'instantiation d'une classe publique (son nom).
    myattrs : Valeurs d'initialisation des différents attributs de l'objet.

Le pointeur sur Class, dans le cas d'une classe privée est fourni au moment de la déclaration de cette dernière (Cf. paragraphe 3.b). Exemple :

mystringgadget = (struct Gadget *)NewObject(NULL, "strgclass", GA_ID, 1L,GA_Left, 0L, GA_Top, 0L, STRINGA_LongVal, 100L, TAG_END);

2.b Destruction de l'objet

Pour détruire un objet, il suffit d'utiliser la procédure :

           void DisposeObject(APTR boopsiobject);

Exemple : DisposeObject(mystringgadget);

2.c Attributs

Un objet se compose de deux parties distinctes :
  • Ses attributs, qui définissent son état à un instant donné. A chacun d'eux est affecté un identificateur unique sous forme d'un entier constant (ULONG).
  • Les méthodes qu'on peut lui appliquer, c'est-à-dire les actions qui peuvent être réalisées sur cet objet. Au même titre que pour les attributs, un entier constant est attaché à chacune de ces méthodes (ULONG).
La lecture et le positionnement d'un attribut se réalisent respectivement par l'intermédiaire de deux fonctions :

ULONG GetAttr (ULONG attrID, APTR myobject, ULONG *mydata);
...où "attrID" est l'identificateur de l'attribut, "myobject" est l'objet BOOPSI source, "mydata" l'adresse de la variable dans laquelle on veut stocker la valeur de l'attribut attrID.

ULONG SetAttrs(APTR myobject, Tag1, Value1, ...);
...où "myobject" est l'objet BOOPSI destination, suivi par la liste des attributs/valeurs, ce qui permet de positionner plusieurs attributs.

Dans le cas d'un objet de type gadget, il est nécessaire de passer plus d'informations en paramètre, d'où l'utilisation de la fonction :

        ULONG SetGadgetAttrs(struct *myobject, struct Window *w, struct
                                      Requester *r, Tag1, Value1, ...);

2.d Méthodes

Toute action sur un objet BOOPSI s'effectue par l'envoi d'un message vers celui-ci. Ce message décrit la méthode qui doit être appliquée sur l'objet et passe les arguments nécessaires à son exécution. Dans les paragraphes précédents, chaque fonction présentée correspond à l'exécution d'une méthode héritée de la classe racine "rootclass". Ainsi, NewObject() envoie une méthode OM_NEW, DisposeObject(): OM_DISPOSE, SetAttrs(): OM_SET, GetAttrs(): OM_GET.

D'autres méthodes existent, spécifiques aux différentes classes. Pour les appliquer, il suffit d'utiliser l'une des deux fonctions de amiga.lib :

        ULONG DoMethodA(Object *myobject, Msg boopsimessage);
        ou 
        ULONG DoMethod(Object* myobject, ULONG methodID, ...);.

Une méthode retourne toujours un ULONG (entier non signé sur 32 bits), qui peut être, suivant les cas, une valeur entière ou un pointeur sur un objet. En règle générale, un retour nul équivaut à un échec.

Exemple : SetAttrs(myobject, myattribute, myvalue)

Peut être réalisé par l'appel suivant :

DoMethod(myobject, OM_SET, myattribute, myvalue, NULL).

3. Création d'une classe BOOPSI

3.a Structure d'une classe

Créer une telle classe est particulièrement aisé. Elle se compose en fait de deux parties distinctes: d'une structure de données correspondant aux attributs de l'objet, et d'une fonction principale ClassDispatcher(Class *class, Object *object, Msg msg); qui reçoit les méthodes et réagit en fonction de la valeur de celles-ci. La classe a donc l'allure suivante :

        /* Class Attributes: Définition */
        struct MyClassData
        {
                ULONG myinteger;
                ...
        }
        /* Class Dispatcher */
        ULONG MyClassDispatcher(Class *cl, Object *obj, Msg msg)
        {
            ...
            switch(msg->MethodID) /* Traitement des différentes méthodes */
            {
                case OM_NEW:
                    ...
                break;
                case OM_SET:
                    ...
                break;
                ...
            }
        }

3.b Initialisation/Libération d'une classe

Lors de la création d'une classe, on doit fournir entre autres : son nom dans le cas où elle est destinée à être publique, le nom de son parent, ainsi que la taille des données qu'elle gère.

On lui fournit ces renseignements lors de son initialisation, à l'appel de la fonction :

        Class * MakeClass(UBYTE *newclassID, UBYTE *pubsuperclassID,
                Class *privsuperclass, UWORD instancesize, ULONG flags).

Si cette classe est publique, newclassID est une chaîne la nommant (NULL si privée) ; les deux champs suivants nomment la classe parente (publique ou privée suivant les cas) ; instancesize donne la taille de l'espace de données qui devra être fourni à chaque objet ; flags (drapeaux) est toujours nul. Ensuite, on initialise les champs de la structure Class de manière à lui indiquer quelle est notre fonction de Dispatch des méthodes.

Remarques :
  • Cette initialisation doit être réalisée avant la création d'une instance de cette classe (un objet), bien entendu. Dans le cas d'une classe externe, l'initialisation est effectuée dans le code d'initialisation de la bibliothèque.
  • Afin d'être ajoutée dans la liste des classes du système, une classe publique doit appeler AddClass(). RemoveClass() permet de l'enlever de cette liste.
  • La libération de la classe s'effectue par l'appel à FreeClass().
  • L'objet (instance de la classe) étant créé, il peut à tout moment avoir accès à son espace de données privé grâce à la fonction void * INST_DATA(Class
  • localclass, Object *object);(<intuition/classes.h>) qui retourne un pointeur sur la structure.
Exemple d'initialisation d'une classe :

    Class * cl;
    if (cl = MakeClass(NULL, "modelclass", NULL, sizeof(MyClassData), 0))
    {
        cl->cl_Dispatcher.h_Entry = HookEntry
                               ; /* HookEntry est défini dans amiga.lib */
        cl->cl_Dispatcher.h_SubEntry = MyClassDispatcher
                               ; /* pointeur sur notre fonction de Dispatch */
    }

3.c Méthodes fondamentales

En règle générale, tout objet doit au moins gérer certaines des méthodes de la classe racine (rootclass), à savoir OM_NEW, OM_SET, OM_GET, OM_DISPOSE. Nous allons donc nous attacher à décrire chacune de ces méthodes et les arguments qu'elles utilisent.

Avant toute chose, il est nécessaire de connaître la fonction DoSuperMethodA(Class *cl, Object *trueclass, Msg msg) de amiga.lib qui applique la méthode à la classe parente. Cette fonction doit être appelée de manière systématique pour les méthodes gérées par les classes parentes, de manière à ce que le traitement soit complet.

OM_NEW : cette méthode est appelée lors de la création de l'objet (fonction NewObject()). Elle doit, dans l'ordre :
  • Créer l'objet parent en appelant la fonction DoSuperMethodA().
  • Initialiser les attributs de l'objet que l'on crée.
Cette méthode reçoit comme paramètre (défini dans <intuition/classusr.h>) :

    struct opSet
    {
        ULONG   MethodID;   /* OM_NEW */
        struct TagItem *ops_AttrList
          ; /* tag liste des attributs à initialiser (couples attribut/valeur) */
        struct GadgetInfo   *ops_GInfo;     /* Toujours NULL */
    }

OM_SET : le but de cette méthode est de mettre à jour les valeurs des attributs de l'objet. Elle est appelée lors de l'utilisation des fonctions SetAttrs() et SetGadgetAttrs() et reçoit, comme OM_NEW, une structure opSet en paramètre. Dans le cas où l'objet est un gadget, ops_GInfo est un pointeur sur une structure GadgetInfo, et est indéfini dans le cas contraire. Votre rôle est de parcourir la liste des attributs et de les positionner un à un (une utilisation judicieuse de la fonction NextTagItem() de la utility.library peut être utile !).

OM_GET : requête pour obtenir la valeur d'un attribut de l'objet. Cette méthode est appliquée lors de l'utilisation de la fonction SetAttrs(). Elle reçoit en paramètre un message (<intuition/classusr.h>) :

    struct opGet
    {
        ULONG MethodID;             /* OM_GET */
        ULONG opg_AttrID;           /* ID de l'attribut */
        ULONG *opg_Storage;         /* pointeur sur la variable dans laquelle 
                                       placer la valeur */
    }

Vous devez vérifier que l'attribut opg_AttrID existe, le renvoyer si c'est le cas, le passer à la superclasse dans le cas contraire.

OM_DISPOSE : méthode appliquée à la libération de l'objet. Effectuez les libérations mémoires nécessaires et passez le message à la superclasse.

OM_NOTIFY : en général traité par une classe parent. Vous devez juste appeler la fonction Dispatcher de la superclasse.

Pour finir, voici d'autres méthodes de rootclass :
  • OM_ADDMEMBER : ajout d'un objet à la liste exec de l'objet BOOPSI concerné (un objet BOOPSI contient une structure MinNode).
  • OM_REMMEMBER : enlever un objet de la liste "personnelle" de l'objet.
  • OM_UPDATE : même chose que OM_SET, si ce n'est que ce message ne doit jamais être utilisé par un programme, mais uniquement par des objets BOOPSI.
Je tiens à préciser qu'il s'agit d'une description incomplète : un cours sur BOOPSI ne tient malheureusement pas sur l'espace qui nous est réservé !

J'imagine que l'ensemble de l'article doit paraître obscur, en particulier à ceux qui ne sont pas habitués à la programmation objet. Pour les autres, vous remarquerez qu'il s'agit en fait d'une implantation dynamique du concept d'objets, certes moins complète que ce que proposent des langages dédiés, mais tout de même fort intéressante pour les programmeurs C que nous sommes ! Dans le prochain article, vous aurez le droit à un (superbe !) exemple qui illustrera tous ces propos.


[Retour en haut] / [Retour aux articles] [Article précédent] / [Article suivant]