IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #21
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    111
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2021
    Messages : 111
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    j'ai juste étudié son système de protection de la mémoire, qui même s'il est efficace, me semble inutilement "compliqué".
    "inutilement compliqué" par rapport à quelle utilisation?
    On n'est peut-être plus contraint aux seuls fork du C, mais faire de la programmation multithread ou concurrente bas niveau reste une tâche complexe. Rust avec son typage avec sémantique de prêt a grandement facilité ces paradigmes de programmation. Je parle de bas niveau ; car sinon, de nombreux langages à GC offrent des facilités pour la programmation multithread/concurrente (Erlang, Go, Clojure, Java, Scala/Akka, .NET, ...).

    Citation Envoyé par OuftiBoy Voir le message
    Les développeurs qui ont des problèmes avec les pointeurs sont souvent des développeurs ayant été formés sur des langage avec GC, et n'ont qu'une vague connaissance du bas-niveau des choses.
    J'ai commencé assez jeune avec du 6502, du 86 en mode réel et du C. Pour autant, je reconnais honnêtement qu'il m'arrive de faire des erreurs dans ma gestion de la mémoire, lorsque j'en ai la responsabilité (C / C++). Ne soyons pas prétentieux!

  2. #22
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Ce n'était pas mon intention...
    fdecode

    Citation Envoyé par fdecode Voir le message
    "inutilement compliqué" par rapport à quelle utilisation?
    On n'est peut-être plus contraint aux seuls fork du C, mais faire de la programmation multithread ou concurrente bas niveau reste une tâche complexe. Rust avec son typage avec sémantique de prêt a grandement facilité ces paradigmes de programmation. Je parle de bas niveau ; car sinon, de nombreux langages à GC offrent des facilités pour la programmation multithread/concurrente (Erlang, Go, Clojure, Java, Scala/Akka, .NET, ...).
    Tout dépend des besoins, on est bien d'accord, la sécurité qu'apporte Rust peut même rendre les choses plus facile, je parlais plutôt des "concepts" utilisés pour cette sécurisation. Mais pour du "bas niveau", C reste indispensable, non pas que Rust ne puisse pas y être très utile, mais tout simplement parce que l'on est (actuellement) toujours certains d'avoir un compilateur C pour tout microcontrôleur, ce qui n'est pas (à l'instant) le cas de Rust. Je parle ici de très petits microcontrôleurs...

    Citation Envoyé par fdecode Voir le message
    J'ai commencé assez jeune avec du 6502, du 86 en mode réel et du C. Pour autant, je reconnais honnêtement qu'il m'arrive de faire des erreurs dans ma gestion de la mémoire, lorsque j'en ai la responsabilité (C / C++). Ne soyons pas prétentieux!
    Je me suis mal expliqué, tout le monde fait des erreurs, moi aussi évidemment, ce n'est pas ce que je voulais dire. Sans prétention aucune, une personne qui n'a été formé qu'avec des langages avec GC ont plus de soucis avec le bas niveau. Je n'ai pas voulu être prétentieux, du tout.

    BàV et Peace & Love.

  3. #23
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Re
    Citation Envoyé par fdecode Voir le message
    Il y a de « l'héritage multiple » au sens des traits, et ils ne sont pas limités en nombre de niveaux. Mais dans la pratique, on utilise beaucoup la composition en Rust.
    Oui, la composition est a préférer sur l'héritage, et l'héritage multiple est a éviter autant que possible.

    Citation Envoyé par fdecode Voir le message
    Comprendre qu'on n'avait pas besoin de classes et d'héritages, ça a aussi été une découverte pour moi, avec Rust.
    Je suis étonné que vous ayez découvert cela avec Rust, car de nombreux excellent bouquin traitant du C++ (par exemple), mettent très vite en avant qu'il faut privilégier la composition à l'héritage, et coder pour des interface et non pour des types "concrets".

    Attention, ce n'est pas un "jugement" (je ne me permettrait pas), mais un "étonnement".

    BàV et Peace & Love.

  4. #24
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    111
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2021
    Messages : 111
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Je suis étonné que vous ayez découvert cela avec Rust
    Il y a une marge entre le fait de ne pas utiliser des héritages multiples de classes (hors classes abstraites pures) tout en limitant sa hiérarchie de classe -- discipline qu'on acquière très vite avec la pratique -- et le fait de ne pas utiliser de classe du tout et de passer à une philosophie trait. C'est une manière assez différente de coder. On ne le perçoit pas si on fait peu de programmation objet, mais quand on a une certaine habitude des classes, il y a quelques réflexes à changer.

    En fait, j'avais aussi en tête les mécanismes de conditionnement de type de Rust qui donnent sa puissance à la composition, mais qui sont plus riches que ça, car permettent aussi des implémentations et définitions conditionnelles. Tout cela se complète bien, et constitue ce paradigme un peu exotique qui m'avait surpris par rapport aux langages à classes.

    Prenons un exemple de la librairie std:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    pub trait From<T>: Sized {
        fn from(value: T) -> Self;
    }
     
    pub trait Into<T>: Sized {
        fn into(self) -> T;
    }
     
    impl<T, U> Into<U> for T
    where U: From<T>, {
        fn into(self) -> U { U::from(self) }
    }
    Qu'est-ce que cela nous dit? On a deux mécanismes de conversion de types qui sont définis par les traits From et Into. Ils sont tout à fait équivalents, mais duaux.
    Par contre, sur cet exemple, la librairie définit une implémentation automatique de Into<U> pour des types T a priori inconnus, si U implémente From<T> ; c'est le impl<T, U: From<T>> Into<U> for T ....
    Donc, ce n'est pas un mécanisme d'héritage au sens où on n'a pas de relation d'héritage "From hérite de Into" (ici, cela n'a pas de sens), par contre, on obtient le comportement de Into<U> par une sorte de mixin contractuel automatique.

  5. #25
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Je suis bien d'accord...
    fdecode.

    Citation Envoyé par fdecode Voir le message
    Il y a une marge entre le fait de ne pas utiliser des héritages multiples de classes (hors classes abstraites pures) tout en limitant sa hiérarchie de classe -- discipline qu'on acquière très vite avec la pratique -- et le fait de ne pas utiliser de classe du tout et de passer à une philosophie trait. C'est une manière assez différente de coder. On ne le perçoit pas si on fait peu de programmation objet, mais quand on a une certaine habitude des classes, il y a quelques réflexes à changer.
    Je suis 100% d'accord.

    Citation Envoyé par fdecode Voir le message
    En fait, j'avais aussi en tête les mécanismes de conditionnement de type de Rust qui donnent sa puissance à la composition, mais qui sont plus riches que ça, car permettent aussi des implémentations et définitions conditionnelles. Tout cela se complète bien, et constitue ce paradigme un peu exotique qui m'avait surpris par rapport aux langages à classes.

    Prenons un exemple de la librairie std:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    pub trait From<T>: Sized {
        fn from(value: T) -> Self;
    }
     
    pub trait Into<T>: Sized {
        fn into(self) -> T;
    }
     
    impl<T, U> Into<U> for T
    where U: From<T>, {
        fn into(self) -> U { U::from(self) }
    }
    Qu'est-ce que cela nous dit? On a deux mécanismes de conversion de types qui sont définis par les traits From et Into. Ils sont tout à fait équivalents, mais duaux. Par contre, sur cet exemple, la librairie définit une implémentation automatique de Into<U> pour des types T a priori inconnus, si U implémente From<T> ; c'est le impl<T, U: From<T>> Into<U> for T ....

    Donc, ce n'est pas un mécanisme d'héritage au sens où on n'a pas de relation d'héritage "From hérite de Into" (ici, cela n'a pas de sens), par contre, on obtient le comportement de Into<U> par une sorte de mixin contractuel automatique.
    Je n'ai pas encore suffisamment de maîtrise de Rust. Mais si ces mécanismes peuvent réduire voir éliminer l'héritage en grande partie, et privilégie la "composition", ça ne saurait qu'être une bonne chose. C'est un peu la même chose que mon idée "d'apis" et de types "respectant cette apis", me semble-t-il. Bon, la syntaxe est "barbare", mais comme tout syntaxes, il faut un peu d'habitude pour s'y faire et ce n'est pas un obstacle insurmontable.

    BàT et Peace & Love.

  6. #26
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 710
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 710
    Par défaut
    Les traits sont surtout une approche plus légère de l'héritage, avec moins de hiérarchies contraignantes.
    La principale différence technique entre les classes et les traits est que ces derniers n'embarquent pas d'état, ce qui évite les principaux problèmes engendrés par l'héritage multiple.

  7. #27
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Bonjour Uther


    Citation Envoyé par Uther Voir le message
    Les traits sont surtout une approche plus légère de l'héritage, avec moins de hiérarchies contraignantes.
    La principale différence technique entre les classes et les traits est que ces derniers n'embarquent pas d'état, ce qui évite les principaux problèmes engendrés par l'héritage multiple.
    C'est une bonne chose, l'héritage multiple je n'ai jamais été fan, j'ai toujours évité. Il faudrait que je me penche plus sur les traits de Rust, et voir ce qu'ils peuvent apporter à Home sans que celui soit trop compliqués à **comprendre** pour le développeur, et que le code source reste lisible et limpide.

    Merci de participer Uther, je prend bien note de vos remarques à tous et je tente d'en faire un "ensemble" cohérent dans Home.

    Là je suis en train de me pencher sur l'implémentation du **concept** de coroutines... Ce n'est pas un concept compliqué en apparence pour l'utilisateur, mais cela n'est pas la chose la plus aisée a ajouter au compilateur...

    Merci de vos retours, cela m'aide beaucoup, et me "recadre" parfois. J'avance bien, mais l'ensemble "nouveau langage" / "compilateur 100% fait main", c'est un gros boulot, sans oublier la documentation. Mais je suis persévérant En tout cas, j'apprend énormément, et rien que ça, ça vaut la peine.

    BàV et Peace & Love les amis.

  8. #28
    Membre éclairé
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    542
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 542
    Par défaut
    Les traits ressemblent aux interfaces Java et à tes api… tu définis des traits (signature d’un ensemble de méthodes), puis les implémentent librement pour les classes que tu souhaites.

    Après, si tu souhaites créer une fonction dont l’argument doit implémenter deux traits

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     fn util<T: TraitA + TraitB>(obj: T) {
        obj.foo(); // accès à TraitA
        obj.bar(); // accès à TraitB
    }
    La principale différence est peut-être que tu ne souhaites pas définir explicitement un trait par fonction mais que ce soit implicite.

    Pour les coroutines, cela nécessite en général de l’assembleur, la commande qui commute d’une coroutine à l’autre doit sauver l’état de la pile (registre SP/Stack Pointer), et le return passe alors automatiquement dans l’autre coroutine (registre PC/Program Counter sauvé sur la pile). Après tu as aussi des appels systèmes à réimplémenter (faire des read/write non bloquants. Si erreur EWOULDBLOCK et s’il y a une coroutine non bloqué on commute dessus… et sinon, un select ou poll pour attendre que que quelque chose se passe dans une des entrées/sorties, puis commuter sur la coroutine qui peut lire ou écrire.

    Les coroutines peuvent être implémentées en bibliothèque… pas besoin de toucher le compilateur… juste qu’il y ait une porte vers l’assembleur (FFI…) mais cela sera de toute manière utile.

  9. #29
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Bonjour Floyer


    Citation Envoyé par floyer Voir le message
    Les traits ressemblent aux interfaces Java et à tes api… tu définis des traits (signature d’un ensemble de méthodes), puis les implémentent librement pour les classes que tu souhaites.
    Oui, c'est très proche des traits tel que tu me les présente, mis à part que je n'ai pas la notion de "classe". Je n'ai que des types, dont les méthodes se voient ajouté un 1er argument "me", pour désigner une instance de type en particulier,

    Citation Envoyé par floyer Voir le message
    Après, si tu souhaites créer une fonction dont l’argument doit implémenter deux traits

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     fn util<T: TraitA + TraitB>(obj: T) {
        obj.foo(); // accès à TraitA
        obj.bar(); // accès à TraitB
    }
    Actuellement, ce n'est pas une via une fonction qui implémente la notion de "traits", mais le simple respect par un type d'une méthode ou plusieurs méthodes d'une api. Mais une api/mtd peuvent (naturellement) appeler d'autres méthode ou api.

    Citation Envoyé par floyer Voir le message
    La principale différence est peut-être que tu ne souhaites pas définir explicitement un trait par fonction mais que ce soit implicite.
    Si, c'est en effet explicite, une api est implémentée via une méthode du type.

    Citation Envoyé par floyer Voir le message
    Pour les coroutines, cela nécessite en général de l’assembleur, la commande qui commute d’une coroutine à l’autre doit sauver l’état de la pile (registre SP/Stack Pointer), et le return passe alors automatiquement dans l’autre coroutine (registre PC/Program Counter sauvé sur la pile).
    Là ou j'en suis, je n'ai pas besoin de l'assembleur au niveau du code utilisateur (et s'il en faut, je repenserai la manière de faire ou même de ne pas faire, car cela serait trop compliqué pour Home, dont le code source doit rester lisible, ce qui ne serait plus le cas s'il faut de l'assembleur dans le code de l'utilisateur, qui de plus rendrait le code difficilement portable pour une autre "architecture"). C'est au final de l'assembleur, car le code source est "compilé" vers un "assembleur" pour un CPU "virtuelle" (en passant par un IRC).

    Citation Envoyé par floyer Voir le message
    Après tu as aussi des appels systèmes à réimplémenter (faire des read/write non bloquants. Si erreur EWOULDBLOCK et s’il y a une coroutine non bloqué on commute dessus… et sinon, un select ou poll pour attendre que que quelque chose se passe dans une des entrées/sorties, puis commuter sur la coroutine qui peut lire ou écrire.
    Le langage "repose" sur un µKernel RTOS, dont les "appel système" se feront via une INT software. Le µKernel pourrait devoir supporter cela, via des mutex et ou des sémaphores.

    Citation Envoyé par floyer Voir le message
    Les coroutines peuvent être implémentées en bibliothèque… pas besoin de toucher le compilateur… juste qu’il y ait une porte vers l’assembleur (FFI…) mais cela sera de toute manière utile.
    Actuellement, j'utilise des **mots-clefs**, pas de bibliothèque. Comme toujours, je ferais le constat de mes choix, et s'ils ne sont pas les bons, je réfléchirais à un meilleur système.

    J'ai parfois l'impression que je joue à l'équilibriste, entre "lisibilité" et "possibilités" du langage. Honnêtement, c'est parfois difficile de trouver le juste milieux. Cela allonge le temps de développement, mais je n'ai pas de "date" butoir, mais j'avance quand même plus que je ne recule...

    Merci à Vous.

    BàV et Peace & Love.

  10. #30
    Membre éclairé
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    542
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 542
    Par défaut
    Rust n’est POO au sens classique. Les traits peuvent être associés à des structures (struct).

    Que tu utilises une bibliothèque FFI avec de l’assembleur ou un mot clé spécial du language qui provoque l’échange de pile ne change pas grand chose (c’est du language machine au final et ce n’est pas portable). RTOS est multithread… il n’y a alors qu’à utiliser les appels systèmes disponibles. Mais c’est du multithread et non des coroutines. Chaque approche a ses avantages.

    Un exemple de bibliothèque de coroutine est Lwt pour OCaml… en fait, tous les appels systèmes bloquant sont encapsulés par la bibliothèque pour permettre d’éviter qu’une coroutine bloque tout le monde sur un tel appel.

  11. #31
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 710
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 710
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Oui, c'est très proche des traits tel que tu me les présente, mis à part que je n'ai pas la notion de "classe". Je n'ai que des types, dont les méthodes se voient ajouté un 1er argument "me", pour désigner une instance de type en particulier,
    Pour le coup, j'ai l'impression que tu as exactement reproduit le fonctionnement de Rust sur ce point. En Rust, il n'a pas de classe non plus. Par contre les types de base (struct, enum et union) peuvent implémenter des méthodes soit directement, soit via un trait ce qui permet le polymorphisme.
    Et comme pour toi la différence entre une méthode et une fonction classique se fait en passant l'élément en premier paramètre (qui se nomme "self")

  12. #32
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Bonjour Uther


    Citation Envoyé par Uther Voir le message
    Pour le coup, j'ai l'impression que tu as exactement reproduit le fonctionnement de Rust sur ce point. En Rust, il n'a pas de classe non plus. Par contre les types de base (struct, enum et union) peuvent implémenter des méthodes soit directement, soit via un trait ce qui permet le polymorphisme.
    Et comme pour toi la différence entre une méthode et une fonction classique se fait en passant l'élément en premier paramètre (qui se nomme "self")
    Si j'ai reproduis le fonctionnement de Rust sur ce point, c'est par pur hasard. Utilisé comme premier argument une instance particulière d'un type, en passant (implicitement) à une "méthode" un premier paramètre ("me", c'est plus cours ) représentant une instance particulière, sans devoir créer de class, a été une de mes première décision, pour ne pas "coupler" un type et ses fonctions, ce qui permettait (via mes apis), de "combiner" un type et ses fonctionnalités, sans héritage. Je sais que cela va à l'encontre de la POO classique (telle qu'en C++ ou en Java), mais d'un côté on prône le "découplage", et de l'autre on "couple" data et fonctions dans une class, c'est un peut un non-sens...

    En fait, en 'C', le premier paramètre à une fonction est souvent un pointeur vers une structure contenant les données (comme par exemple toutes les fonction qui traitent les fichiers commencent par FILE).

    Je trouve cette "solution" plus simple, l'ajout d'une méthode à un type n'a pas à se soucier d'un "type de base", etc...

    Et plus puissant, car on peut avoir bien plus de "combinaisons" possibles, et les "apis" sont là pour "grouper" des fonctions qui, ensembles (comme un class), permettent de résoudre une problématique. Et d'aider le compilateur a savoir si un type implémente bien une api, et peut donc être utilisé de manière hétérogène, via un "usign ... do ..." ou un "do.. usign ...".

    Voilà l'historique du pourquoi du comment

  13. #33
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Bonjour floyer...


    Citation Envoyé par floyer Voir le message
    Rust n’est POO au sens classique. Les traits peuvent être associés à des structures (struct).
    Oui, et je préfère cette approche.

    Citation Envoyé par floyer Voir le message
    Que tu utilises une bibliothèque FFI avec de l’assembleur ou un mot clé spécial du language qui provoque l’échange de pile ne change pas grand chose (c’est du language machine au final et ce n’est pas portable)
    Pas tout à fait, car le compilateur génére un IRC, l'optimise, puis cet IRC peut-être traduit dans un assembleur particulier. Le "code source" est portable, l'IRC est "portable", mais la traduction de l'IRC vers une machine particulière génère un assembleur propre à cette machine (et donc également la partie nécessaire pour les coroutines). Bon, actuellement, je transforme l'IRC un un "assembleur" particulier pour un CPU "virtuelle" qui exécutera dans une VM ce code assembleur. Attention, je ne génère pas un byte code, mais de l'assembleur (MOV, PUSH, POP, LDR, ...). Il ne serait pas impossible (c'est un exemple), de traduire l'IRC vers de l'assembleur 6510 et de générer un jeu pour le C64 (mais je n'en suis pas là).

    Citation Envoyé par floyer Voir le message
    RTOS est multithread… il n’y a alors qu’à utiliser les appels systèmes disponibles. Mais c’est du multithread et non des coroutines. Chaque approche a ses avantages.
    Oui et non. Je m'explique. Le RTOS (real-time) n'utilise pas des "threads", mais de simple "task" avec priorité (pour donner la main à la task "ready" de la plus haute importance), est un simple TCB (tack control bloc). J'en ai déjà fait un (pour un système embarqué), donc ce ne sera pas une trop grande difficulté pour moi que le µKernel inclus cette partie. Les coroutines utiliseront (ou pas, je n'ai pas tranché), les ce qu'offre le RTOS (sémaphore, mutex, etc...). Il y aura donc la possibilité de créer des task, et l'une d'elle pourrait ne s'occuper que des coroutines (par exemple). Mais tout cela est encore un peut loin. Je veux d'abord "finaliser" une V1 (avec ou sans coroutines, et avec un µkernel minimum (sans RTOS)).

    Citation Envoyé par floyer Voir le message
    Un exemple de bibliothèque de coroutine est Lwt pour OCaml… en fait, tous les appels systèmes bloquant sont encapsulés par la bibliothèque pour permettre d’éviter qu’une coroutine bloque tout le monde sur un tel appel.
    C'est effectivement la grande différence. Le RTOS permet de "suspendre" un task, mais dès que les conditions sont réunies, une task peut reprendre la main (enfin, celle qui a la plus haute importance).

    Un RTOS peut "donner la main" à la sortie d'une INT à la tâche la plus importante, c'est la différence en un RTOS "préemptif" et un OS "Coopératif". Je vais voir si c'est faisable/utile/nécessaire de "combiner" les avantages d'un RTOS, et les "avantage" des coroutines

    Oui, je sais, j'ai encore du boulot ;-)

    Là pour le moment, je suis bloqué sur un "bug" concernant le scoping, mais je vais trouver

    Merci à vous.

    BàV et Peace & Love.

  14. #34
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut Re
    Citation Envoyé par Uther Voir le message
    Pour le coup, j'ai l'impression que tu as exactement reproduit le fonctionnement de Rust sur ce point. En Rust, il n'a pas de classe non plus. Par contre les types de base (struct, enum et union) peuvent implémenter des méthodes soit directement, soit via un trait ce qui permet le polymorphisme.
    Et comme pour toi la différence entre une méthode et une fonction classique se fait en passant l'élément en premier paramètre (qui se nomme "self")
    Tous les langages POO (C++/Java/Python) opère de la sorte. En C++, c'est le "this" qui est implicite dans les méthodes d'une class, en java pareil, et en Python, il est "explicite, c'est le "self" qui est toujours ce premier paramètre. La seule différence c'est que Home (et Rust on dirait) n'a pas de notion de class.

  15. #35
    Membre éclairé
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    542
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 542
    Par défaut
    Ada95 est orienté objet, mais les méthodes sont déclarées comme des fonctions normales. Il n’y a pas de variable spéciale comme this ou self. La seule subtilité, est que le premier argument est un tagged record plutôt qu’un record, ce qui permet le polymorphisme dynamique. (NB, le polymorphisme dynamique ne peut pas porter sur plus d’un paramètre comme usuellement)

    Et de même, pour appeler la méthode, pas de objet.method(…), mais method(objet, …) comme une fonction normale.

  16. #36
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut ... je ne savais pas.


    Je n'ai pas "poussé" mon analyse d'ADA autant sur ADA que sur Rust (Rust reprenant des techniques d'ADA). Mais merci de la précision

    Citation Envoyé par floyer Voir le message
    Ada95 est orienté objet, mais les méthodes sont déclarées comme des fonctions normales. Il n’y a pas de variable spéciale comme this ou self.
    Ok, je te crois sur parole.

    Citation Envoyé par floyer Voir le message
    La seule subtilité, est que le premier argument est un tagged record plutôt qu’un record, ce qui permet le polymorphisme dynamique. (NB, le polymorphisme dynamique ne peut pas porter sur plus d’un paramètre comme usuellement)
    Cela "ressemble" au premier paramètre d'une méthode, mais je n'ai aucune idée de ce qu'est un "tagged record", je l'avoue.

    Citation Envoyé par floyer Voir le message
    Et de même, pour appeler la méthode, pas de objet.method(…), mais method(objet, …) comme une fonction normale.
    Là, bien qu'actuellement, dans Home il n'est possible que d'utiliser la syntaxe object.method(). Mais comme je désire une POO plûtot "Orientée Comportement", je réfléchis à 3 solutions, o.m(), m(o,...) ou les 2 ensembles.

    Utiliser la syntaxe m(o, ...) seul, simplifierait (et même supprimerait) toute une partie du compilateur, sans perdre en "fonctionnalité", et la notion même de POO n'aurait pas à être abordées, ce qui rendrait le langage encore plus simple et lisible.

    C'est un peut comme pour les "coroutines", qui sont une "notion" qui semble difficile a expliquer (c'est ce que je constate en regardant les différentes explications que je trouve sur le net), alors que l'on peut avoir le même résultat en utilisant des "tasks", même si ce sont 2 choses différentes, les "tasks" s'expliquent plus facilement que les coroutines, et elle ne sont pas beaucoup plus "lourde" que les coroutines. (je ne parle pas de threads). La seule chose un peu difficile avec les "tasks", c'est d'éviter les "inversions de priorités", et de s'assurer qu'il n'y a pas de "dead lock". Mais ayant déjà réalisé un RTOS, je sais qu'il existe des "techniques" pour s'affranchir de ces soucis. Si je dois choisir entre coroutines et tasks, ma préférence va nettement vers les tasks...

  17. #37
    Membre éclairé
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    542
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 542
    Par défaut
    Je citais plus Ada95 pour l’aspect syntaxique… à voir si l’idée est bonne pour toi.

    Mais si tu veux d’une côté définir des structures, puis plus tard, définir des méthodes qui s’appliquent dessus et pouvoir déclarer une fonction qui fait n’importe quoi qui fait ceci ou cela, le mieux est Rust ou OCaml.

    L’exemple de Rust nécessite de l’explicite (cf la déclaration T:TraitA+TraitB), OCaml de l’implicite (OCaml déduit de a#m1; a#m2, que a DOIT supporter au moins les deux méthodes m1 et m2, et ne compilera pas s’il n’en n’a pas l’assurance. (Contrairement aux languages dynamiques où l’erreur se fera à l’exécution). Mais OCaml permet d’expliciter, avec le type
    < m1 : unit;
    m2 : int >
    Par exemple

  18. #38
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 464
    Par défaut merci du temp que tu prend...
    Citation Envoyé par floyer Voir le message
    Je citais plus Ada95 pour l’aspect syntaxique… à voir si l’idée est bonne pour toi.
    Pour l'instant, je vais rester sur O.m(), avec O donné implicitement en 1er argument d'une méthode d'un type.

    Citation Envoyé par floyer Voir le message
    Mais si tu veux d’une côté définir des structures, puis plus tard, définir des méthodes qui s’appliquent dessus et pouvoir déclarer une fonction qui fait n’importe quoi qui fait ceci ou cela, le mieux est Rust ou OCaml.
    Pour le moment, j'ai aussi des simples "struct". Je pourrais facilement "transformer" une "struct" en "type", avec une syntaxe du genre type player |= struct player, apiXXX, apiYYY.

    A partir de là, tout les accès à la "struct" seront des accès au "type", qui "englobe" tout ce qui est possible de faire avec la simple "struct" (techniquement, elle disparait pour ne pas avoir 2x le même nom, mais comme ce qui est possible avec une struct reste possible avec un type, c'est "transparent" pour l'utilisateur. Au niveau du compilateur, ce sera un peu comme si la struct n'avait jamais existé, mais seulement le type).

    Citation Envoyé par floyer Voir le message
    L’exemple de Rust nécessite de l’explicite (cf la déclaration T:TraitA+TraitB), OCaml de l’implicite (OCaml déduit de a#m1; a#m2, que a DOIT supporter au moins les deux méthodes m1 et m2, et ne compilera pas s’il n’en n’a pas l’assurance. (Contrairement aux languages dynamiques où l’erreur se fera à l’exécution). Mais OCaml permet d’expliciter, avec le type
    < m1 : unit;
    m2 : int >
    Par exemple
    Là aussi, je vais rester sur le fait qu'un type implémente une api pour être utilisé de manière homogène. Là ou je doit encore "trancher", c'est soit obliger toute les "méthodes" d'une apis, ou juste ce qui est utilisés par différents types utilisés d'une manière hétérogène. Je peux faire ça de façon explicite ou implicite, le compilateur n'aura pas de mal à s'y retrouver. Tout comme je peux aussi "restreindre" les types qui peuvent être utilisé de manière homogène.

    Je vais continuer avec le support des coroutines, puis lorsque le µKernel sera opérationnel, je verrais si les tasks peuvent les remplacer, ou si les 2 peuvent coexister, ou si je ne garde que les tasks (si les tasks peuvent remplacer fonctionnellement les coroutines). J'ai besoin d'écrire le code pour voir si je tombe sur un soucis et/ou voir les avantages d'une solution ou de l'autre (à différent niveau), et l'impacte sur la lisibilité du code, et/ou la difficulté qui en découlerait pour le développeur final.

    Encore merci de tes remarques, suggestions, explications et du temps que tu m'accorde.
    Comme je fait l'ensemble tout seul, c'est précieux pour moi d'avoir ce dialogue.

    Ma Roadmap actuelle:
    - implémenter les coroutines (tout ou en partie, car il y a des implémentations offrant plus de services que d'autres).
    - Mettre sur place un µKernel (sans RTOS) (pour l'utiliser par exemple pour faire les allocations dynamiques, et les désallocations automatique et sécurisées)
    - Implémenter quelques "constructions" supplémentaires,
    - Affiner le Checker pour éviter des "fausses alertes",
    - Affiner la production de l'IRC,
    - Affiner la production de l'Assembleur,
    - Affiner le Linker,
    - Binarisé l'assembleur
    - écrire un début de VM qui peut exécuter ce code, faire quelques affichages, etc...

    C'est déjà pas mal. Mais c'est plus du "pratique" que du "théorique".

    Encore merci 1000x.
    BàT et Peace & Love.

  19. #39
    Membre éclairé
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    542
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 542
    Par défaut
    « Là ou je doit encore "trancher", c'est soit obliger toute les "méthodes" d'une apis, ou juste ce qui est utilisés par différents types utilisés d'une manière hétérogène. » de deux choses l’une, soit tu déclares explicitement (façon Rust), soit tu déclares de façon implicite (façon OCaml), mais j’imagine mal un type « cela supporte une méthode m1… et un truc que j’ai oublié ». Autant demander à tout déduire (façon OCaml). Mais l’inférence de type est plus complexe !

Discussions similaires

  1. Réponses: 0
    Dernier message: 31/12/2020, 17h12
  2. Informaticiens de ce forum, quel est votre métier ?
    Par Bidouille dans le forum Emploi
    Réponses: 46
    Dernier message: 21/07/2005, 13h12
  3. Réponses: 32
    Dernier message: 20/01/2004, 20h33
  4. renomer une DB
    Par wilaya dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 12/12/2003, 16h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo