From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.alt.bavardage.raleur,ens.forum.informatique.lang.c
Subject: setjmp() et longjmp()
Date: 12 Jun 1999 20:18:41 GMT
Lines: 45
Sender: madore@clipper.ens.fr
Message-ID: <7jufb1$h39$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 929218721 17513 129.199.129.1 (12 Jun 1999 20:18:41 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 12 Jun 1999 20:18:41 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.alt.bavardage.raleur:176 ens.forum.informatique.lang.c:416

Extrait du man de setjmp() :

#                 The caller of setjmp() must not have returned in
#     the  interim.

Je n'avais jamais utilisé longjmp() et setjmp(), en me disant toujours
que c'était un mauvais style de programmation, qu'il fallait éviter
ces choses-là, mais d'un autre côté j'avais toujours pensé que ça
faisait des miracles et que le jour où j'en aurais besoin elles
seraient là pour me sauver.

Et vlan, je découvre que ce ne sont pas des vraies continuations mais
une espèce de succédané inspipide, qui ne fait pas une copie de la
pile mais seulement du pointeur de pile, donc qui est seulement
capable de quitter des procédures imbriquées et pas d'entrer dedans.

Et je découvre ça juste quand je viens d'écrire un compilateur pour un
langage de mon invention (lequel langage se pose en candidat sérieux
pour le poste de Pire Langage de Tous les Temps), compilateur qui
produit du C en sortie, et que je voulais implémenter la fonction
call/cc dans mon langage.  En gros, si je comprends bien, j'ai le
choix entre renoncer au call/cc ou bien renoncer à produire du C.

Que me conseillez-vous ?  Que font les langages qui (à la différence
de ce truc sous-développé qu'on appelle le C) supportent le call/cc ?
L'ennui c'est que je n'ai pas envie de me taper l'allocation mémoire,
donc produire du code assembleur ça m'emmerde.  Y a-t-il des machines
virtuelles sympathiques pour ce genre de cas ?

Ou encore, y a-t-il des bibliothèques en C qui sont capables de
sauvegarder toute la pile et de faire des vrais call/cc ?  Une
possibilité folle (mais peut-être pas tant que ça finalement) qui me
vient à l'esprit, c'est d'utiliser des threads (à la place de setjmp()
je crée un nouveau thread, qui se gèle aussitôt, et à la place de
longjmp() je dégèle le thread gelé et je fais mourir le thread
courant) : jouable ?

          -- David, qui est content d'avoir réussi à écrire un
             compilateur en quelques heures (enfin, quand on voit la
             gueule du langage, ça n'a rien d'un exploit - finalement
             il m'a fallu plus de temps pour écrire dans un programme
             dans ce langage qui énumère les entiers que pour écrire
             le compilateur lui-même), et qui n'est pas content que
             les limitations idiotes du C viennent lui réduire cet
             exploit à néant.

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang.c
Subject: Re: setjmp() et longjmp()
Date: 13 Jun 1999 11:19:03 GMT
Lines: 119
Sender: madore@clipper.ens.fr
Message-ID: <7k0437$hlj$1@clipper.ens.fr>
References: <7jufb1$h39$1@clipper.ens.fr> <7jv3p7$qvv$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 929272743 18099 129.199.129.1 (13 Jun 1999 11:19:03 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 13 Jun 1999 11:19:03 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang.c:423

Valentin Bonnard in litteris (informatique.lang.c:422) scripsit :
> C'est très bas niveau. Ça sert notament a implémenter (mal) 
> les exceptions.

« Bas niveau » + « C » = pléonasme.

> En fait, c'est encore pire: longjmp ne garanti pas la 
> restaurations des variables automatiques non volatiles, 
> en particulier si elles sont mises en registres.

Ce qui est logique.  Mais ça, à la limite, je m'en fous, je peux
toutes les déclarer volatile : vu la vitesse à laquelle mes trucs sont
censés tourner, ça ne va pas changer grand-chose.

> Bien sur ! Tu crois au miracle ?

Non, je suis les conseils de fortune :

« Do not believe in miracles -- rely on them. »

> Les continuations de quasiment tous les autres langages 
> ne font pas mieux (sauf bien sur Scheme).

Ah.  Certes.

>> Et je découvre ça juste quand je viens d'écrire un compilateur pour un
>> langage de mon invention (lequel langage se pose en candidat sérieux
>> pour le poste de Pire Langage de Tous les Temps),
> 
> Avec CALL FROM ?

Non.  C'est un langage sans variables, sans aucun type de données (pas
de type entier notamment), d'ailleurs sans entiers non plus, sans
registres, sans branchements conditionnels (sans aucune forme de saut,
d'ailleurs).  Pour dévoiler ce mystère, c'est du lambda-calcul non
typé pur mais écrit sans l'opération lambda (lambda, c'est mal, ça
introduit des variables), avec uniquement les opérations K = x -> y ->
x et S = x -> y -> z -> (xz)(yz) (et je suis gentil, j'autorise aussi
l'identité).  Ce n'est peut-être pas aussi tordu que Malebolge, mais
c'est moins ad hoc, et c'est au moins aussi chiant à écrire vu que
j'ai passé plusieurs bonnes heures sur le programme qui énumère les
entiers naturels (avec des lignes pleines d'astérisques) :

-----
````s``s`ks``s`k`si``s`kk``s`k
                             `s``s`ksk # Increment
                            ``s`k
                                ``s``s``si`kp`kri # Print n (and return n)
                               i`ki
  `ki # The number zero (replace by i to start from one)
 ``s``s`ks``s`k`si``s`kk``s`k # Ditto (other half-loop)
                            `s``s`ksk
                           ``s`k
                               ``s``s``si`kp`kri
                              i`ki
-----

> Je suis content de te voir dire du mal de C.

At your service.  Tu veux que je dise un peu de mal de C++ aussi ?

> Enfin en C il faut pas copier la pile, parce que les effets 
> de bord seraient perdus. Tu utilise des effets de bords en C ?

Je ne suis pas sûr de comprendre.  C'est quoi exactement les effets de
bord ?

En fait, comme je le disais, je pourrais produire un call/cc
apparemment parfait avec des threads, si seulement pthread_create() ne
déclarait pas de manière fasciste que le thread doit commencer une
nouvelle pile toute vierge :

typedef struct {
  pthread_t thr;
  pthread_mutex_t mut;
  pthread_cond_t cond;
  int ret;
} david_jmp_buf[1];

int
david_setjmp (david_jmp_buf env)
{
  pthread_t son;

  env[0].thr = pthread_self ();
  pthread_mutex_init (&env[0].mut, NULL);
  pthread_cond_init (&env[0].cond, NULL);
  env[0].ret = 0;
  pthread_create (&son, NULL, don_t_create_a_new_stack_you_fascist_\
just_copy_the_current_one_and_leave_me_right_here_where_i_am);
  if ( pthread_equal (pthread_self (), son) )
    return 0;
  pthread_mutex_lock (&env[0].mut);
  while ( ! env[0].ret )
    pthread_cond_wait (&env[0].mut, &env[0].cond);
  pthread_mutex_unlock (&env[0].mut);
  pthread_mutex_destroy (&env[0].mut);
  pthread_cond_destroy (&env[0].cond);
  return env[0].ret;
}

void
david_longjmp (david_jmp_buf env, int val)
{
  /* val != 0 */
  pthread_mutex_lock (&env[0].mut);
  env[0].ret = val;
  pthread_cond_signal (&env[0].cond);
  pthread_exit (NULL);
}

> Je croyait que tu _installait_ TECO, pas que tu le 
> réécrivait.

Apparemment, TECO est trop utilisable et trop maniable puisque Jean a
réussi à écrire un quine dedans.  Il faut prendre des mesures de
rétortion.

          -- David

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang.c
Subject: Re: setjmp() et longjmp()
Date: 13 Jun 1999 18:22:49 GMT
Lines: 65
Sender: madore@clipper.ens.fr
Message-ID: <7k0stp$44g$1@clipper.ens.fr>
References: <7jufb1$h39$1@clipper.ens.fr> <7k0plo$1gd$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 929298169 4240 129.199.129.1 (13 Jun 1999 18:22:49 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 13 Jun 1999 18:22:49 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang.c:434

call-with-current-continuation, principalement en Scheme.

L'idée c'est que (call/cc foobar) va appeler la fonction foobar avec
comme argument la « continuation courante ».  Cet objet magique
ressemble à une fonction mais lorsqu'on l'appelle il se produit un saut
non-local dans le programme jusqu'au retour de call/cc qui semble alors
retourner (une nouvelle fois éventuellement) avec comme valeur de
retour le paramètre qu'on a passé à la continuation.  (Par ailleurs, si
foobar retourne normalement, call/cc retournera la valeur retournée par
foobar.)  Le call/cc est un peu l'analogue du setjmp() du C (sauf que
call/cc peut faire entrer dans des fonctions et pas seulement sortir)
et la condinuation l'analogue du longjmp() : quand on appelle
longjmp(), la fonction setjmp() retourne une nouvelle fois avec comme
valeur de retour la valeur passée à longjmp().  Si on veut, ça
ressemble aussi à une création de thread, mais une création synchrone :
la continuation représente un thread sur le point de quitter le call/cc
(avec comme valeur de retour la valeur passée à la dite continuation),
mais l'exécution de ce thread étant exclusive de l'exécution
principale.

Par exemple,

guile> (define call/cc call-with-current-continuation)
;; guile ne définit pas cette abréviation contrairement à la plupart
;; des autres Schemes
guile> (define cont1 #f)
guile> (call/cc (lambda (cont) (set! cont1 cont) 42))
;; Ici, la fonction foobar prend la continuation et la stocke dans
;; cont1, puis renvoie 42.
42
guile> cont1
#<continuation 905 @ 806f5a0>
guile> (cont1 1729)
;; La continuation correspond nous renvoie au call/cc qui l'a
;; produite, et ce call/cc allait revenir au toplevel.  Si on applique
;; la continuation au nombre 1729, on fait comme si le call/cc de tout
;; à l'haure renvoyait 1729.
1729
guile> (+ (cont1 1729) 1)
;; Il s'agit d'une sortie non-locale qui renvoie immédiatement au
;; toplevel.
1729
guile> (define cont2 #f)
guile> (if (call/cc (lambda (cont) (set! cont2 cont) #f))
...        (display "You have been continued!\n"))      
;; La continuation cont2 écrit "You have been continued!" si on
;; l'applique à #t et elle ne fait rien si on l'applique à #f.
guile> cont2
#<continuation 946 @ 8070670>
guile> (cont2 #t)
You have been continued!
guile> (cont2 #f)

Petit jeu amusant : expliquer pourquoi l'appel

((call/cc call/cc) (call/cc call/cc))

est une boucle infinie.  Expliquer pourquoi ce n'est pas le cas si on
fait la chose en apparence identique suivante :

(define call2 (call/cc call/cc))

puis

(call2 call2)

