Re: Tutoriel coroutines

Top Page

Reply to this message
Author: Edgar Bonet
Date:  
To: guilde
Subject: Re: Tutoriel coroutines
Bonjour !

Frédéric a écrit :
> Je me suis remis à la programmation assembleur Z80 en faisant un
> reboot du jeu d'aventures que j'avais développé (et commercialisé) au
> début des années 90, sur Amstrad CPC.


Tiens ! Je serais curieux de voir ça. ;-)

> Quelqu'un aurait-il un bon tutoriel [...] sur le concept des
> coroutines ?


Je ne suis pas spécialiste, mais je me souviens avoir lu un article en
anglais sur les « protothreads », qui sont des sortes de coroutines
légères, qui n'impliquent pas des changements de contexte. Ça présentait
une implémentation en C basée uniquement sur le préprocesseur. L'idée
est d'écrire un code qui a l'air purement séquentiel, avec des appels à
YIELD(), et laisser le préprocesseur transformer ça en une machine à
états. La variable d'état correspond à la ligne où il faut reprendre
l'exécution au prochain appel. Chaque YIELD() met à jour cette variable
et fait un simple return.

De mémoire, tu écrivais un truc comme ça :

    void my_coroutine(void)
    {
        COROUTINE_START();
        foo();
        YIELD();
        bar();
        YIELD();
        baz();
        COROUTINE_END();
    }


et les macros le transformaient en ça :

    void my_coroutine(void)
    {
        const int couroutine_start_line = __LINE__ + 3;
        static int couroutine_next_line = couroutine_start_line;
        switch (couroutine_next_line) {
            case __LINE__:
                foo();
                couroutine_next_line = __LINE__ + 2;
                return;
            case __LINE__:
                bar();
                couroutine_next_line = __LINE__ + 2;
                return;
            case __LINE__:
                baz();
        }
        couroutine_next_line = couroutine_start_line;
    }


en un peu plus compliqué pour gérer des cas pathologiques.

> Du coup, je pense que la fonction 'print' se prêterait bien aux
> coroutines : à la fin de chaque caractère affiché, je rend la main à
> la boucle principale, et je continue où j'en étais à l'appel suivant.


Là, pour le coup, si tu réécris ça en machine à états à la main, je n'ai
pas l'impression que ça ajoute beaucoup de complexité. Ça revient à
transformer ça :

    void print(const char *message)
    {
        while (*message) draw_character(*message++);
    }


en ça :

    /* Call with NULL to print the second and subsequent chars. */
    void print(const char *message)
    {
        static const char *p;      // state variable
        if (message) p = message;  // start a new message
        if (*p) draw_character(*p++);
    }


À+,

Edgar.