; .EnTete "Le-Lisp (c) version 15.2" " " "Les structures C"
; .EnPied " " "%" " "
; .Chapitre S "Les structures C"
;
; .Centre "*****************************************************************"
; .Centre " Ce fichier est en lecture seule hors du projet ALE de l'INRIA.  "
; .Centre " Il est maintenu par ILOG SA, 2 Avenue Gallie'ni, 94250 Gentilly "
; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA                 "
; .Centre "*****************************************************************"

; .Centre "$Header: cstruct.ll,v 4.2 88/12/07 22:43:47 kuczynsk Exp $"

#|
Ce chapitre de'crit la manipulation des structures C en Lisp sur les machines
UNIX a` mots de 32 bits adressant l'octet.

Il est possible de manipuler en Lisp, de la me↑me manie`re que les structures
Lisp, des objets alloue's par des programmes C.

L'utilisation des fonctions de'crites ci-dessous demande quelques 
pre'cautions\ :
  - Lorsque l'on stocke un objet Lisp dans une structure C il faut toujours
garder un pointeur Lisp sur cet objet afin que le Garbage Collector
(qui ne va pas visiter le contenu des structures C) ne re'cupe`re pas l'objet.
  - Il vaut mieux ne pas stocker de chai↑nes de caracte`res Lisp dans les 
structures C. Le Garbage Collector risquant de de'placer la chai↑ne de 
caracte`res dans le tas Lisp, Le pointeur C ne serait plus a` jour.
Il est pre'fe'rable d'allouer une chai↑ne de caracte`res dans le tas C
et d'y recopier la chai↑ne Lisp (voir la fonction CSTRING)

Il faut aussi noter que
la lecture et l'e'criture des champs des structures C ne sont pas
des ope'rations tre`s efficaces. Si l'on de'sire manipuler intensivement
les structures C il vaut mieux e'crire les parties de
programmes concerne'es directement en C.

L'interface avec les objets C
utilise les primitives d'appel de proce'dures externes, 
de'crites au chapitre 14 du manuel de re'fe'rence. Elles permettent
notamment de de'finir en Lisp diverses fonctions de'crites a` la fin
de ce chapitre et permettant\ :
  - l'allocation de structures C dans une zone me'moire externe a` Le←Lisp
(fonctions MALLOC et SIZEOF).
  - la libe'ration de ces structures (fonction FREE)
  - l'acce`s indexe' au niveau du caracte`re, du petit entier et du pointeur
en lecture et en e'criture dans les structures C (fonctions LONG/SHORT/CHARREF
et LONG/SHORT/CHARSET).

.Section "De'finition de Structures C en Lisp"
.Fonction "(DEFCSTRUCT <nom> (<taille1> <champ1>)...(<tailleN> <champN>)) \ \ \ [MACRO a` N arguments]"
de'finit une structure C. <nom> est un symbole qui est le nom de la structure
C. Les arguments
<champI> sont des symboles nommant les champs, les arguments <tailleI> 
sont des symboles de'crivant la taill me'moire du i-e`me champ. 
Les symboles <tailleI> peuvent prendre l'une des trois valeurs suivantes\ :
  - LONG\ : le champ a alors la taille d'un pointeur
  - SHORT\ : le champ a alors la taille d'un entier court
  - CHAR\ : le champ a alors la taille d'un caracte`re.

DEFCSTRUCT positionne enfin l'indicateur SIZEOF sur la PLIST du symbole
<nom>, qui indique, en nombre de caracte`res, la taille me'moire occupe'e
par une instance de la structure C (voir la fonction SIZEOF).

.Fonction "(#:<nom>:MAKE)    [SUBR a` 0 argument]"
Alloue dans le tas C et retourne en valeur une nouvelle instance de
la structure C <nom>. La fonction #:<nom>:MAKE est la fonction de
cre'ation spe'cifique a` la structure <nom>. Elle est automatiquement
de'finie par la fonction DEFCSTRUCT. L'objet cre'e' est un pointeur hors de
la zone me'moire re'serve'e a` l'interpre`te Lisp, repre'sente' sous la forme
d'un CONS de deux petits entiers.

.DefLL "#:<nom>:MAKE"
    (DE #:<nom>:MAKE ()
        (←MALLOC (SIZEOF '<nom>)))
.FinLL

.Fonction "(#:<nom>:<champI> <o> [<e>])    [SUBR a` 1 ou 2 arguments]"
<nom> est le nom d'une structure C de'finie par la fonction pre'ce'dente,
<champI> est le nom d'un champ de cette
structure. Les fonctions #:<nom>:<champI> sont les fonctions d'acce`s aux
champs des instances de la structure C <nom>. Elles sont de'finies
automatiquement par la fonction DEFCSTRUCT.

.Fonction "(SIZEOF <nom>) \ \ \ [SUBR a` 1 argument]"
<nom> est un symbole repre'sentant le nom d'une structure C pre'de'finie 
pre'cedemment par la fonction DEFCSTRUCT. Retourne en valeur la taille 
me'moire utilise'e en me'moire par une instance de la structure C <nom>.
Cette taille peut e↑tre fourni a` la fonction ←MALLOC pour allouer un vecteur
d'objets C de type nom. Pour allouer un seul objet C on peut utiliser la 
fonction #:<nom>:MAKE.

.pp
\fIExemple\ :\fP
 Voici la de'claration C et la de'claration Lisp de la me↑me structure 
FontInfo\ :
.DebLL
typedef struct ←FontInfo {
	Font id;
	short height, width, baseline, fixedwidth;
	unsigned char firstchar, lastchar;
	short *widths;		/* pointer to width array in OpenFont */
} FontInfo;

(defcstruct FontInfo
       (long id)
       (short height)
       (short width)
       (short baseline)
       (short fixedwidth)
       (char firstchar)
       (char lastchar)
       (long widths))
.FinLL

Voici la traduction en Lisp d'un programme C manipulant cette structure
(on voit que la manipulation du vecteur de petits entiers fontinfo->widths
ne'cessite la fonction ←WORDREF, de'crite plus bas)\ :
.DebLL
show←font (fontinfo) FontInfo *fontinfo; {
char c;
       printf("Height %d, Width %d, Baseline %d\n",
                    fontinfo->height,
                    fontinfo->width,
                    fontinfo->baseline);
       if (fontinfo->fixedwidth)
          printf("Fixedwidth font.");
          else
            for (c = fontinfo->firstchar; c <= fontinfo->lastchar; c++){
                printf("  %c %d pixels\n", fontinfo->widths[c]);
}

(de show←font (fontinfo)
    (prinf "Height ~D, Width ~D, Baseline ~D"
           (#:FontInfo:height fontinfo)
           (#:FontInfo:width fontinfo)
           (#:FontInfo:baseline fontinfo))
    (terpri)
    (if (neqn (#:FontInfo:fixedwidth fontinfo) 0)
        (print "Fixedwidth font.")
        (for (c (#:FontInfo:firstchar fontinfo)
                1
                (#:FontInfo:lastchar fontinfo))
             (prinf "  ~C ~D pixels" 
                    c
                    (←shortref (#:FontInfo:widths fontinfo) (add c c)))
             (terpri))))

.FinLL
.pp
Ces fonctions sont de'finies en Lisp de la manie`re suivante\ :

|#
(unless (>= (version) 15.2)
        (error 'load 'erricf 'cstruct))

(add-feature 'cstruct)

(when (eq 0 (getglobal '|←charref|))
      (cload (catenate #:system:system-directory "o/cstruct.o")))

(setq #:sys-package:colon 'cstruct)

(dmd defcstruct (name . fields)
     (let ((i 0) res)
          (setq fields
                (mapcar (lambda (field)
                                (prog1 (cons i field)
                                       (incr i (cassq (car field)
                                                      '((char . 1)
                                                        (short . 2)
                                                        (long . 4))))))
                        fields))
          (while fields
                 (newl res
                       (:field-accessor name (nextl fields))))
          `(progn ,@res 
                  (putprop ',name ,i 'sizeof)
                  (de ,(symbol name 'make) ()
                      (|←malloc| ,i))
                  ',name)))

(de :field-accessor (name (index size field-name))
    `(de ,(symbol name field-name) (o . v)
         (ifn v
              (,(concat "←" size "ref") o ,index)
              (,(concat "←" size "set") o ,index (car v)))))

(de sizeof (cstruct)
    (getprop cstruct 'sizeof))

#|
.Section "Les Fonctions Primitives de Manipulation des Objets C"
L'interface avec les structures C est base'e sur les fonctions primitives
suivantes. Ces fonctions peuvent e↑tre utiles pour la manipulation des
vecteurs d'objets C, comme de'crit dans l'exemple ci-dessous. On leur 
pre'fe`rera lorsque cela est possible les fonctions de la section 
pre'ce'dente.

.Fonction "(←MALLOC <size>) \ \ \ [SUBR a` 1 argument]"
Alloue <size> octets dans le tas C et retourne un pointeur externe (CONS de 
deux petits entiers pointant sur la zone alloue'e.

.Fonction "(←FREE <external>) \ \ \ [SUBR a` 1 argument]"
Rend au tas C les pointeur externe <external>. <External> doit evoir e'te'
alloue' par la fontionc ←MALLOC, ou bien par une proce'dure C utilisant
la fonction C malloc.

.Fonction "(←CHARREF <external> <index>) \ \ \ [SUBR a` 2 arguments]
.Fonction "(←SHORTREF <external> <index>) \ \ \ [SUBR a` 2 arguments]
.Fonction "(←LONGREF <external> <index>) \ \ \ [SUBR a` 2 arguments]
<external> est un pointeur dans le tas C, ou bien sur une structure C rec,ue
d'un programme C exte'rieur a` Le←Lisp. Retourne les 8(CHAR), 16(SHORT) ou 
32(LONG) bits pris a` l'adresse <external>+<index>, <index> e'tant exprime'e
en octets.

.Fonction "(←CHARSET <external> <index> <value>) \ \ \ [SUBR a` 3 arguments]
.Fonction "(←SHORTSET <external> <index> <value>) \ \ \ [SUBR a` 3 arguments]
.Fonction "(←LONGSET <external> <index> <value>) \ \ \ [SUBR a` 3 arguments]
<external> est un pointeur dans le tas C, ou bien sur une structure C rec,ue
d'un programme C exte'rieur a` Le←Lisp. Stocke les 8(CHAR), 16(SHORT) ou 
32(LONG) bits les moins significatifs de la valeur <value> a` l'adresse
<external>+<index>, <index> e'tant exprime'e en octets.

.Fonction "(←CSTRING <string>) \ \ \ [SUBR a` 1 argument]"
Alloue (add1 (LEN <string>)) caracte`res dans le tas C, y recopie le contenu
de la chai↑ne Lisp <string> et retourne un pointeur sur la zone me'moire 
alloue'e. Cette fonction permet de recopier dans le tas C les chai↑nes de
caracte`res Lisp, ce uqi est ne'cessaire si l'on de'sire passer a` C des
chai↑ne qui ne doivent pas e↑tre de'place'es elors des re'cupe'rations.
.DebLL
ex:
; La proce'dure C XMenuInsertSelection rec,oit rec,oit une chai↑ne de
; caracte`res de'crivant le nom d'un item a` rajouter au menu. Cette chai↑ne
; ne doit pas bouger lors des re'cupe'rations Lisp. Il faut donc la recopier 
; dans le tas C avant de la passer a` la proce'dure

/* fichier C */
int XMenuInsertSelection (menu, pane, selection, data, label, active)
XMenu *menu;
int pane, selection;
caddr←d data;
char *label;
int active;
\&...

#\&| fichier Lisp |\&#

(defextern ←XMenuInsertSelection (external fix fix t external fix) fix)
; on donne le type external a` l'argument label (char *)

(de make-menu strings 
 ...
    (←XMenuInsertSelection menu 0 0 0 (←cstring (nextl strings)) 1)
 ...
.FinLL

.pp
Malloc et Free sont les fonctions C du manuel de re'fe'rence UNIX (3).
Les autres fonctions
sont des de'finitions externes des fonctions C de'crites ci-dessous\ :
.DebLL
charref (s, n) char *s; {
    return * (char *) (s + n);
}

shortref (s, n) char  *s; {
    return * (short *) (s + n);
}

longref (s, n) char  *s; {
    return * (int *) (s + n);
}

charset (s, n, v) char *s; {
    *(char *) (s + n) = v;
}

shortset (s, n, v) char   *s; {
    *(short *) (s + n) = v;
}

longset (s, n, v) char   *s; {
    *(int *) (s + n) = v;
}

char *cstring (lispstring, length) char *lispstring; {
char *s = (char *)malloc (length+1);
     strncpy (s, lispstring, length+1);
     return s;
}
.FinLL
|#

(defextern |←malloc| (fix) external)
(defextern |←free| (external))

(defextern |←charref| (external fix))
(defextern |←shortref| (external fix))
(defextern |←longref| (external fix) external)
(defextern |←charset| (external fix fix))
(defextern |←shortset| (external fix fix))
(defextern |←longset| (external fix external))

(defextern |←cstring| (string fix) external)