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.
- #ifndef _SOO_H
- #define _SOO_H
- #include "Object.h"
- #ifdef __STDC__
- extern class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages );
- extern value sendmsg( void *rcv, message msg, ... );
- extern value applymsg( void *rcv, message msg, va_list va);
- extern value supersend( class c, void *rcv, message msg, ... );
- extern value superapply( class c, void *rcv, message msg, va_list va );
- #else
- extern class defclass();
- extern value sendmsg();
- extern value applymsg();
- extern value supersend();
- extern value superapply();
- #endif
- #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
- #include "So-o.h"
Inclut les déclarations des fonctions de So-o et de la classe Object.
- class Hello;
Définit la classe Hello, une variable globale.
- static instance i_hello(instance self) {
- printf( "Hello from So-o!\n" );
- return self;
- }
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
.
- void defclassHello() {
- selector _i_messages[] = {
- "hello", METHOD(i_hello),
- 0, 0
- };
- Hello = defclass("Hello", 0, 1, 0, 0, 0, _i_messages);
- }
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
- #include "So-o.h"
- #include <stdlib.h>
- extern class Hello;
- extern void defclassHello();
- int main( int argc, char *argv[] ) {
- instance hello;
- defclassHello();
- hello = (instance)sendmsg(Hello, "new").p;
- sendmsg(hello, "hello");
- sendmsg(hello, "free");
- exit( 0 );
- }
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
- 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.
- if (!Object)
- defclassObject();
Construit la classe Object si la variable globale Object
vaut 0.
- 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
.
- class_send_message(c, "initialize");
Envoie le message initialize à la nouvelle classe.
- return c;
- }
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
- value sendmsg( void *rcv, message msg, ... ) {
- va_list va;
- va_start(va, msg);
- value ret = applymsg(rcv, msg, va);
- va_end(va);
- return ret;
- }
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
- value applymsg( void *rcv, message msg, va_list va ) {
- #ifdef TRYCATCH
- static jmp_buf toplevel;
- static unsigned calldepth = 0;
- if (calldepth == 0) {
- if (setjmp(toplevel) != 0) {
- calldepth = 0;
- fprintf(stderr, BadReceiver, "sendmsg", msg, rcv);
- fprintf(stderr, "\n");
- return (value)0;
- }
- }
- if (!rcv)
- longjmp(toplevel, 1);
- ++calldepth;
- #endif
- value ret;
- switch (((instance)rcv)->type) {
- case INSTANCE:
- ret = object_send_message_va(rcv, msg, va);
- break;
- case CLASS:
- ret = class_send_message_va(rcv, msg, va);
- break;
- default:
- #ifdef TRYCATCH
- longjmp(toplevel, 1);
- #else
- ret = (value)0;
- break;
- #endif
- }
- #ifdef TRYCATCH
- --calldepth;
- #endif
- return ret;
- }
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
- value supersend( class c, void *rcv, message msg, ... ) {
- va_list va;
- va_start(va, msg);
- value ret = superapply( c, rcv, msg, va );
- va_end(va);
- return ret;
- }
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
- value superapply( class c, void *rcv, message msg, va_list va ) {
- #ifdef TRYCATCH
- static jmp_buf toplevel;
- static unsigned calldepth = 0;
- if (calldepth == 0) {
- if (setjmp(toplevel) != 0) {
- calldepth = 0;
- fprintf(stderr, BadReceiver, "supersend", msg, rcv);
- fprintf(stderr, "\n");
- return (value)0;
- }
- }
- if (!c)
- longjmp(toplevel, 1);
- if (!rcv)
- longjmp(toplevel, 1);
- ++calldepth;
- #endif
- value ret;
- switch (((instance)rcv)->type) {
- case INSTANCE:
- ret = object_super_send_message_va(c, rcv, msg, va);
- break;
- case CLASS:
- ret = class_super_send_message_va(c, rcv, msg, va);
- break;
- default:
- #ifdef TRYCATCH
- longjmp(toplevel, 1);
- #else
- ret = (value)0;
- break;
- #endif
- }
- #ifdef TRYCATCH
- --calldepth;
- #endif
- return ret;
- }
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.
int i;
long l;
float f;
double d;
void *p;
} value;
- #include "So-o.h"
- 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.
- static class c_initialize(class self) {
- return sendmsg(self, "set", "count", 0).p;
- }
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
.
- static instance c_new(class self, va_list va) {
- instance i = superapply(X, self, "new", va).p;
- sendmsg(self, "set", "count", sendmsg(self, "get", "count").i + 1);
- return i;
- }
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
.
- static int c_count(class self) {
- return sendmsg(self, "get", "count").i;
- }
c_count
retourne le nombre d'instances de X.
- static void i_free(instance self) {
- class c = sendmsg(self, "class").p;
- sendmsg(c, "set", "count", sendmsg(c, "count").i - 1);
- supersend(X, self, "free");
- }
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.
- static instance i_init(instance self, va_list va) {
- int value = va_arg(va, int);
- supersend(X, self, "init");
- sendmsg(self, "set", "value", value);
- return self;
- }
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.
- static value i_value(instance self) {
- return sendmsg(self, "get", "value");
- }
i_value
retourne la valeur d'une instance de X.
- void defclassX() {
- property _c_properties[] = {
- "count",
- 0
- };
- property _i_properties[] = {
- "value",
- 0
- };
- selector _c_messages[] = {
- "initialize", METHOD(c_initialize),
- "new", METHOD(c_new),
- "count", METHOD(c_count),
- 0, 0
- };
- selector _i_messages[] = {
- "free", METHOD(i_free),
- "init", METHOD(i_init),
- "value", METHOD(i_value),
- 0, 0
- };
- X = defclass("X", 0, 1, _c_properties, _i_properties, _c_messages, _i_messages);
- }
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
- #include "So-o.h"
- #include <stdlib.h>
- extern class X;
- extern void defclassX();
- int main( int argc, char *argv[] ) {
- instance x1, x2;
- defclassX();
- printf("count=%d\n", sendmsg(X, "count").i);
- x1 = (instance)sendmsg(X, "new", 1).p;
- printf("x1=%d\n", sendmsg(x1, "value").i);
- x2 = (instance)sendmsg(X, "new", 2).p;
- printf("x2=%d\n", sendmsg(x2, "value").i);
- printf("count=%d\n", sendmsg(X, "count").i);
- sendmsg(x1, "free");
- printf("count=%d\n", sendmsg(X, "count").i);
- sendmsg(x2, "free");
- printf("count=%d\n", sendmsg(X, "count").i);
- exit( 0 );
- }
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
Commentaires