21

Interface

L'interface fonctionnelle de So-o en C est constituée de 3 fonctions : defclass qui définit une nouvelle classe, sendmsg qui sert systématiquement à envoyer un message à une classe ou à une instance, et supersend qui exécute une méthode héritée d'une superclasse.

Tout le code de l'interface de So-o est dans le fichier So-o.c.

  1. #ifndef _SOO_H
  2. #define _SOO_H
  3.  
  4. #include "Object.h"
  5.  
  6. #ifdef __STDC__
  7. extern class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages );
  8. extern value sendmsg( void *rcv, message msg, ... );
  9. extern value applymsg( void *rcv, message msg, va_list va);
  10. extern value supersend( class c, void *rcv, message msg, ... );
  11. extern value superapply( class c, void *rcv, message msg, va_list va );
  12. #else
  13. extern class defclass();
  14. extern value sendmsg();
  15. extern value applymsg();
  16. extern value supersend();
  17. extern value superapply();
  18. #endif
  19. #endif

Le fichier So-o.h déclare les fonctions de l'interface de So-o. Il inclut le fichier Object.h qui inclut le fichier OL.h.

defclass
SYNOPSIS

class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages )

DESCRIPTION

defclass définit une classe.

name spécifie le nom de la classe. name doit être un nom de variable C valide.

superclass est la référence globale de la superclasse de la nouvelle classe. Si superclass vaut 0, la nouvelle classe hérite par défaut de la classe Object définie par la variable globale Object. defclass construit automatiquement la classe Object si nécessaire.

revision donne le numéro de révision de la classe. revision est un int > 0 qui peut servir à différencier des versions successives de la classe.

class_properties et instance_properties listent les propriétés de la classe et des instances de la classe. Une propriété est une chaîne de caractères terminée par un '\0'. Une liste de propriétés est un tableau terminé par un pointeur NULL. Un tableau NULL définit une liste vide.

class_messages et instance_messages sont des listes associatives de messages et de méthodes de la classe et des instances de la classe. Un message est une chaîne de caractères terminée par un '\0'. Une méthode est un pointeur sur une fonction convertie par la macro METHOD définie dans OL.h. Un sélecteur associe une méthode à un message. Une liste de messages et de méthodes est un tableau terminé par deux pointeurs NULL. Un tableau NULL définit une liste vide.

defclass retourne un pointeur sur une struct _class allouée qui doit être assigné à la variable globale name. Remarquez qu'une classe peut être redéfinie.

Une nouvelle classe reçoit automatiquement le message initialize. NOTE : La méthode de classe initialize définie par la classe Object ne fait rien.

EXEMPLE
  1. #include "So-o.h"

Inclut les déclarations des fonctions de So-o et de la classe Object.

  1. class Hello;

Définit la classe Hello, une variable globale.

  1. static instance i_hello(instance self) {
  2.     printf( "Hello from So-o!\n" );
  3.  
  4.     return self;
  5. }

Définit le code du message d'instance hello. Conventionnellement, une méthode est une fonction qui a comme nom un message d'instance préfixé par i_ pour une méthode d'instance ou par c_ pour une méthode de classe. Une méthode de classe ou d'instance est statique. Une méthode a toujours comme premier argument la classe ou l'instance qui reçoit le message. Conventionnellement, cet argument a pour nom de variable self. Une méthode qui n'a rien de particulier à retourner retourne généralement self.

  1. void defclassHello() {
  2.     selector _i_messages[] = {
  3.         "hello",    METHOD(i_hello),
  4.         0, 0
  5.     };
  6.  
  7.     Hello = defclass("Hello", 0, 1, 0, 0, 0, _i_messages);
  8. }

Construit la classe Hello et l'associe à la variable globale Hello. La classe Hello hérite par défaut de la classe Object. Son numéro de révision vaut 1. Elle n'a pas de propriétés de classe, de messages de classe ni de propriétés d'instance. Elle a un message d'instance : hello.

TEST
  1. #include "So-o.h"
  2.  
  3. #include <stdlib.h>
  4.  
  5. extern class Hello;
  6.  
  7. extern void defclassHello();
  8.  
  9. int main( int argc, char *argv[] ) {
  10.     instance hello;
  11.  
  12.     defclassHello();
  13.  
  14.     hello = (instance)sendmsg(Hello, "new").p;
  15.  
  16.     sendmsg(hello, "hello");
  17.  
  18.     sendmsg(hello, "free");
  19.  
  20.     exit( 0 );
  21. }

Inclut les déclarations des fonctions et des types de données de So-o. Déclare la classe Hello et son constructeur. Initialise la classe Hello. Envoie le message new à la classe Hello pour créer l'instance hello en lui affectant le pointeur de l'union retournée par sendmsg. Envoie le message hello à l'instance hello pour afficher le message de bienvenue. Envoie le message free à l'instance hello pour libérer l'espace alloué par cette instance.

$ gcc -O -c test-Hello.c
$ gcc -O -c Hello.c
$ gcc test-Hello.o Hello.o libso-o.a -o test-Hello
$ test-Hello
Hello from So-o!
CODE
  1. class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages ) {

defclass prend 7 arguments. name spécifie le nom de la classe. superclass est la référence globale de la superclasse de la nouvelle classe. revision donne le numéro de révision de la classe. class_properties, instance_properties, class_messages et instance_messages listent les propriétés et les sélecteurs de la classe et des instances de la classe.

  1.     if (!Object)
  2.         defclassObject();

Construit la classe Object si la variable globale Object vaut 0.

  1.     class c = class_new(name, superclass, revision, class_properties, instance_properties, class_messages, instance_messages);

Crée une struct _class avec la fonction class_new définie par l'Object Layer avec les paramètres de l'appel de defclass.

  1.     class_send_message(c, "initialize");

Envoie le message initialize à la nouvelle classe.

  1.     return c;
  2. }

Retourne la classe.

sendmsg
SYNOPSIS

value sendmsg( void *rcv, message msg, ... )

DESCRIPTION

sendmsg retourne le résultat de l'envoi du message msg et de ses paramètres à l'instance ou à la classe rcv.

Le type de retour de sendmsg est value, une union.

CODE
  1. value sendmsg( void *rcv, message msg, ... ) {
  2.     va_list va;
  3.  
  4.     va_start(va, msg);
  5.  
  6.     value ret = applymsg(rcv, msg, va);
  7.  
  8.     va_end(va);
  9.  
  10.     return ret;
  11. }

sendmsg construit la variable va de type va_list avec les paramètres qui suivent msg et appelle applymsg avec rcv, msg et va en argument.

applymsg
SYNOPSIS

value applymsg( void *rcv, message msg, va_list va )

DESCRIPTION

applymsg retourne le résultat de l'envoi du message msg avec les paramètres dans va à la classe ou à l'instance rcv.

Appeler applymsg directement est une option qui peut être vitale lorsque du code doit passer des arguments entre des méthodes.

CODE
  1. value applymsg( void *rcv, message msg, va_list va ) {
  2. #ifdef TRYCATCH
  3.     static jmp_buf toplevel;
  4.     static unsigned calldepth = 0;
  5.  
  6.     if (calldepth == 0) {
  7.         if (setjmp(toplevel) != 0) {
  8.             calldepth = 0;
  9.  
  10.             fprintf(stderr, BadReceiver, "sendmsg", msg, rcv);
  11.             fprintf(stderr, "\n");
  12.  
  13.             return (value)0;
  14.         }
  15.     }
  16.  
  17.     if (!rcv)
  18.         longjmp(toplevel, 1);
  19.  
  20.     ++calldepth;
  21. #endif
  22.  
  23.     value ret;
  24.  
  25.     switch (((instance)rcv)->type) {
  26.         case INSTANCE:
  27.             ret = object_send_message_va(rcv, msg, va);
  28.             break;
  29.         case CLASS:
  30.             ret = class_send_message_va(rcv, msg, va);
  31.             break;
  32.         default:
  33. #ifdef TRYCATCH
  34.             longjmp(toplevel, 1);
  35. #else
  36.             ret = (value)0;
  37.             break;
  38. #endif
  39.     }
  40.  
  41. #ifdef TRYCATCH
  42.     --calldepth;
  43. #endif
  44.  
  45.     return ret;
  46. }

applymsg appelle la fonction class_send_message ou object_send_message selon le type de rcv, CLASS ou INSTANCE, avec rcv, msg et va en argument. applymsg retourne la valeur retournée par class_send_message ou object_send_message.

Compilé avec TRYCATCH, applymsg détecte si rcv est NULL ou n'est pas une classe ni une instance et fait sauter le code en arrière vers un point sûr juste avant le premier appel à applymsg dans la pile. Ce mode est utile pour analyser un programme qui s'écrase à cause d'une faute de mémoire probablement causée par une référence défectueuse à une classe ou une instance.

supersend
SYNOPSIS

value supersend( class c, void *rcv, message msg, ... )

DESCRIPTION

supersend retourne le résultat de l'envoi du message msg et de ses paramètres à l'instance ou à la classe rcv dans le contexte de la superclasse de la classe c.

Le type de retour de supersend est value, une union.

supersend sert généralement à appeler la version héritée d'une méthode qui est redéfinie par une sous-classe. Dans ce cas, c est la classe de la méthode qui redéfinit msg et appelle supersend.

CODE
  1. value supersend( class c, void *rcv, message msg, ... ) {
  2.     va_list va;
  3.  
  4.     va_start(va, msg);
  5.  
  6.     value ret = superapply( c, rcv, msg, va );
  7.  
  8.     va_end(va);
  9.  
  10.     return ret;
  11. }

value supersend construit la variable va de type va_list avec les paramètres qui suivent msg et appelle superapply avec c, rcv, msg et va en argument.

superapply
SYNOPSIS

value superapply( class c, void *rcv, message msg, va_list va )

DESCRIPTION

superapply retourne le résultat de l'envoi du message msg avec les paramètres dans va à la classe ou à l'instance rcv dans le contexte de la superclasse de la classe c.

Appeler superapply directement est une option qui peut être vitale lorsque du code doit passer des arguments entre des méthodes.

CODE
  1. value superapply( class c, void *rcv, message msg, va_list va ) {
  2. #ifdef TRYCATCH
  3.     static jmp_buf toplevel;
  4.     static unsigned calldepth = 0;
  5.  
  6.     if (calldepth == 0) {
  7.         if (setjmp(toplevel) != 0) {
  8.             calldepth = 0;
  9.  
  10.             fprintf(stderr, BadReceiver, "supersend", msg, rcv);
  11.             fprintf(stderr, "\n");
  12.  
  13.             return (value)0;
  14.         }
  15.     }
  16.  
  17.     if (!c)
  18.         longjmp(toplevel, 1);
  19.  
  20.     if (!rcv)
  21.         longjmp(toplevel, 1);
  22.  
  23.     ++calldepth;
  24. #endif
  25.  
  26.     value ret;
  27.  
  28.     switch (((instance)rcv)->type) {
  29.         case INSTANCE:
  30.             ret = object_super_send_message_va(c, rcv, msg, va);
  31.             break;
  32.         case CLASS:
  33.             ret = class_super_send_message_va(c, rcv, msg, va);
  34.             break;
  35.         default:
  36. #ifdef TRYCATCH
  37.             longjmp(toplevel, 1);
  38. #else
  39.             ret = (value)0;
  40.             break;
  41. #endif
  42.     }
  43.  
  44. #ifdef TRYCATCH
  45.     --calldepth;
  46. #endif
  47.  
  48.     return ret;
  49. }

superapply appelle la fonction class_super_send_message ou object_super_send_message_va selon le type de rcv, CLASS ou INSTANCE, avec c, rcv, msg et va en argument. applymsg retourne la valeur retournée par class_super_send_message ou object_super_send_message.

Compilé avec TRYCATCH, applymsg détecte si c est NULL et si rcv est NULL ou n'est pas une classe ni une instance et fait sauter le code en arrière vers un point sûr juste avant le premier appel à superapply dans la pile. Ce mode est utile pour analyser un programme qui s'écrase à cause d'une faute de mémoire probablement causée par une référence défectueuse à une classe ou une instance.

EXEMPLE

Le premier argument d'une méthode est toujours la classe ou l'instance qui reçoit le message. Si une méthode attend des paramètres, elle doit les extraire de la va_list que l'Object Layer reçoit de applymsg ou superapply et passe à une méthode en second argument.

Une méthode peut retourner un des types de donnée qui est un membre de l'union value définie dans OL.h. Pour assigner à une variable ou directement passer la valeur retournée par le traitement d'un message, le code doit accéder au membre de l'union correspond au type de données attendu.

typedef union _value {
    int i;
    long l;
    float f;
    double d;
    void *p;
} value;
X.c
  1. #include "So-o.h"
  2.  
  3. class X;

Inclut les déclarations des fonctions et des types de données de So-o. Définit la classe X, une variable globale.

  1. static class c_initialize(class self) {
  2.     return sendmsg(self, "set", "count", 0).p;
  3. }

c_initialize initialise la propriété count de la classe à 0 quand la classe est construite. NOTE : Le message initialize est automatiquement envoyé à une nouvelle classe par defclass.

  1. static instance c_new(class self, va_list va) {
  2.     instance i = superapply(X, self, "new", va).p;
  3.  
  4.     sendmsg(self, "set", "count", sendmsg(self, "get", "count").i + 1);
  5.  
  6.     return i;
  7. }

c_new crée une instance de X en envoyant le message new dans le contexte de la classe Object, la superclasse de la classe X, puis incrémente count avant de retourner la nouvelle instance. Remarquez comment la classe qui implémente la méthode et la liste des arguments est passée à superapply.

  1. static int c_count(class self) {
  2.     return sendmsg(self, "get", "count").i;
  3. }

c_count retourne le nombre d'instances de X.

  1. static void i_free(instance self) {
  2.     class c = sendmsg(self, "class").p;
  3.  
  4.     sendmsg(c, "set", "count", sendmsg(c, "count").i - 1);
  5.  
  6.     supersend(X, self, "free");
  7. }

i_free décrémente count puis exécute le message dans le contexte de la classe Object. Remarquez comment une méthode d'instance envoie un message à sa classe.

  1. static instance i_init(instance self, va_list va) {
  2.     int value = va_arg(va, int);
  3.  
  4.     supersend(X, self, "init");
  5.  
  6.     sendmsg(self, "set", "value", value);
  7.  
  8.     return self;
  9. }

i_init exécute le message init dans le contexte de la classe Object puis initialise value à la valeur du paramètre value du message. Les paramètres d'une méthode sont toujours passés dans un va_list. NOTE : Le message init est automatiquement envoyé à une nouvelle instance par new.

  1. static value i_value(instance self) {
  2.     return sendmsg(self, "get", "value");
  3. }

i_value retourne la valeur d'une instance de X.

  1. void defclassX() {
  2.     property _c_properties[] = {
  3.         "count",
  4.         0
  5.     };
  6.     property _i_properties[] = {
  7.         "value",
  8.         0
  9.     };
  10.     selector _c_messages[] = {
  11.         "initialize",   METHOD(c_initialize),
  12.         "new",          METHOD(c_new),
  13.         "count",        METHOD(c_count),
  14.         0, 0
  15.     };
  16.     selector _i_messages[] = {
  17.         "free",     METHOD(i_free),
  18.         "init",     METHOD(i_init),
  19.         "value",    METHOD(i_value),
  20.         0, 0
  21.     };
  22.  
  23.     X = defclass("X", 0, 1, _c_properties, _i_properties, _c_messages, _i_messages);
  24. }

Initialise la classe X. La classe X hérite de la classe Object. Elle a 1 propriété  : count, le nombre d'instances qu'elle a créées. Une instance a 1 propriété : value, sa valeur. La classe X redéfinit les messages initialize et new. Elle implémente le message count. Une instance redéfinit les messages free et init. Elle implémente le message value.

TEST
  1. #include "So-o.h"
  2.  
  3. #include <stdlib.h>
  4.  
  5. extern class X;
  6.  
  7. extern void defclassX();
  8.  
  9. int main( int argc, char *argv[] ) {
  10.     instance x1, x2;
  11.  
  12.     defclassX();
  13.  
  14.     printf("count=%d\n", sendmsg(X, "count").i);
  15.  
  16.     x1 = (instance)sendmsg(X, "new", 1).p;
  17.     printf("x1=%d\n", sendmsg(x1, "value").i);
  18.  
  19.     x2 = (instance)sendmsg(X, "new", 2).p;
  20.     printf("x2=%d\n", sendmsg(x2, "value").i);
  21.  
  22.     printf("count=%d\n", sendmsg(X, "count").i);
  23.     sendmsg(x1, "free");
  24.     printf("count=%d\n", sendmsg(X, "count").i);
  25.     sendmsg(x2, "free");
  26.     printf("count=%d\n", sendmsg(X, "count").i);
  27.  
  28.     exit( 0 );
  29. }

Initialise la classe X. Affiche le compte des instances. Crée une instance de X avec la valeur 1. Affiche sa valeur. Crée une instance de X avec la valeur 2. Affiche sa valeur. Affiche le compte des instances. Libère la première instance. Affiche le compte des instances. Libère la seconde instance. Affiche le compte des instances.

$ gcc -I/usr/local/include/so-o X.c test-X.c -lso-o -o test-X
$ test-X
count=0
x1=1
x2=2
count=2
count=1
count=0
VOIR AUSSI

Object Layer, Object, Une partie de poker

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].