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

Langages de programmation Discussion :

« Home » : réflexions sur un nouveau langage


Sujet :

Langages de programmation

  1. #21
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 479
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 479
    Par défaut
    Clairement, vous n'avez aucune notion de ce qui se fait actuellement car toutes vos critiques tombent complètement à côté, no offense.

    Avant de pouvoir créer un jeu 2D via Python ou Pygames, il y a une série d'obstacle a franchir.
    On parle de sites Web qui demandent au maximum un login/password qu'on peut mettre en favori, aussi bien pour Scratch que pour des IDE online de Python. Aucune chance qu'une VM se lance plus vite qu'une page Web "sobre", et encore moins sur un petit coucou.
    Avec un langage totalement visuel comme Scratch qui s'adresse à de très jeunes enfants (bien moins que 14 ans), conçu avec l'aident de pédagogues spécialisés (mais majoritairement américains), votre Home est à des années-lumière de complexité de Scratch.
    Python a été choisi par l'éducation Nationale (qui continue à nous coller du Grec et du Latin à la place de l'algorithmie, merci la politique et la reproduction sociale) avec d'autres intentions que de faire plaisir à des cocorico-industrieux à la Minitel.
    Oui, l'éducation Nationale, c'était clairement pas mieux avant et les inspecteurs académiques et autres concepteurs de programmes éducatifs ne sont pas des politicards de Ministre en recherche du soutien des "vieux" (avec droit de vote).

    Ces 2 langages disposent chacun d'une ludothèque colossale toute en "Open Source".
    Avec des communautés de "jeunes" énorme et ils ont de la "hype".
    Et on parle même pas de truc comme Roblox, etc...

    Que Home ait un intérêt, peut-être, mais il doit être évaluer à l'aulne de ce qui ce fait actuellement, pour qu'il se positionne "correctement".

    https://scratch.mit.edu/projects/editor/
    https://www.online-python.com/
    etc...

  2. #22
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Oui et non...
    Bonjour

    Citation Envoyé par bacelar Voir le message
    Clairement, vous n'avez aucune notion de ce qui se fait actuellement car toutes vos critiques tombent complètement à côté, no offense.
    Je ne suis pas d'une génération qui se victimise en permanance pour un oui ou pour un nom. Donc pas de soucis de ce côté là. Ce que vous nommé "critique", je le nomme "constat". Je n'ai peut-être "aucune notion de ce qui se fait actuellement", soit. Quel est le problème avec cela ?

    Citation Envoyé par bacelar Voir le message
    On parle de sites Web qui demandent au maximum un login/password qu'on peut mettre en favori, aussi bien pour Scratch que pour des IDE online de Python.
    Je ne veux pas de "site web", je n'ai aucune attirance pour le web et ce qu'il est devenu. En 30 ans, on est passé de site "Web" où l'échange était possible, où des informations sérieuses et de qualités étaient la norme, à maintenant une vaste plateforme d'abrutissement, où l'on trouve 1 bon contenu pour 20 stupidités sans nom, le tout en étant traqué, et matraqué de publicité. Si on parle "technique", le développement web est une calamité, qui s'importe petit à petit sur le "Desktop". Bref, on est pas obligé d'être tous d'accord.

    Citation Envoyé par bacelar Voir le message
    Aucune chance qu'une VM se lance plus vite qu'une page Web "sobre", et encore moins sur un petit coucou.
    J'en doute fortement. Des pages "sobre", il y'en a de moins en moins. Une distro du genre TinyCore se charge en RAM, à partir d'un SD card ou d'une clef USB bien plus rapidement qu'un Windows ou qu'un Linux Mint. Considérer des PC de 5 ans sont bons pour la décharge, je trouve cela un gâchi immense. Mais encore une fois, si j'arrive jusque là, tant mieux, sinon, tant pis. Je n'ai pas d'ambitions particulière. C'est d'abord et avant tout un projet perso. Vous pouvez le trouver "stupide", "hord du temps", d'une "autre époque", c'est votre droit. Tout comme c'est le miens de voir les choses différemment.

    Citation Envoyé par bacelar Voir le message
    Avec un langage totalement visuel comme Scratch qui s'adresse à de très jeunes enfants (bien moins que 14 ans), conçu avec l'aident de pédagogues spécialisés (mais majoritairement américains), votre Home est à des années-lumière de complexité de Scratch.
    Je ne pense pas avoir dit du mal de Scratch. Si oui, merci de me montrer quand où j'ai exprimé cela. Scratch, a certainement un attrait, de par son côté visuelle, je n'ai pas dis le contraire. Mon gamin, via Minecraft et ses mécanismes, faisait celà il y a plus de 10 ans.

    Les "pédagogues spécialisés", veuillez m'excuser, mais ils ont boussillé toute une génération, avec des idées délirantes. L' idéologie de l'enfant-roi a été un désastre total. L'école est venu une garderie. Il suffit de comparer des archives, et le "niveau" scolaire est en chute libre depuis des lustres. Un enfant de 14 ans il y a 50 ans savait former une phrase correcte, avait un vocabulaire bien plus étendu, est une pensée bien mieux structurée qu'un jeune de 20 ans qui sait à peine parler correctement, avec 200 mots de vocabulaire max. Merci les "pédagogues spécialisés". Ma mère étaient institutrice maternelle, et constatait, même chez de très jeunes enfant de 6 ans un recul de leurs capacités d'année en année. Merci les "pédagogues spécialisés".

    Citation Envoyé par bacelar Voir le message
    Python a été choisi par l'éducation Nationale (qui continue à nous coller du Grec et du Latin à la place de l'algorithmie, merci la politique et la reproduction sociale) avec d'autres intentions que de faire plaisir à des cocorico-industrieux à la Minitel.
    Oui, Python est très certainement un bon choix, ai-je-dis le contraire ? Le Minitel était un délire "Franco-Français", tout comme "l'informatique pour tous" à l'époque de "Laurent Fabius", qui a été très longtemps à la tête de votre "Conseil d'état" qui se permet de "refuser" des lois votées par vos parlementaires.

    Si vous ne comprennez pas l'intérêt du Grec ou du Latin, c'est très dommage. Avant de faire de l'algorithmie, il faudrait peut-être apprendre à savoir compter jusque 10 et savoir exprimer une pensée claire. Nombres d'enseignant se plaignent que les enfants ne comprennent même pas les questions qu'on leurs poses. Il savent à peine lire ou écrire. Et il n'y a pas moyen de faire de l'algorithmie si on ne sait pas structurer le début d'une pensée.

    Citation Envoyé par bacelar Voir le message
    Oui, l'éducation Nationale, c'était clairement pas mieux avant et les inspecteurs académiques et autres concepteurs de programmes éducatifs ne sont pas des politicards de Ministre en recherche du soutien des "vieux" (avec droit de vote).
    On voit aujourd'hui le résultat des idéologies promuent par ces derniers. Plus de respect, plus de tenue, plus de connaissance dans les matières de base, le Français et les Mathématiques.

    Citation Envoyé par bacelar Voir le message
    Ces 2 langages disposent chacun d'une ludothèque colossale toute en "Open Source".
    Avec des communautés de "jeunes" énorme et ils ont de la "hype".
    Et on parle même pas de truc comme Roblox, etc...
    Ai-je dis le contraire ? Je n'ai pas l'ambition de me mettre en concurrence avec ceux-ci. Avec personne d'ailleurs. Je ne cherche pas cela. Ne me prêté pas des intentions que je n'ai pas.

    Ils ont de la "Hype" dites-vous. En voilà du beau Français... J'ai clairement dit que c'était un projet "perso" sans ambition particulière. Certains ont comme hobbit de regarder des séries, moi, actuellement, c'est de m'intéresser aux langages informatiques, et aux techniques de compilations. Je n'en ai rien à faire de la "Hype". Ai-je le droit de choisir mes hobbits ? Ou bien suis-je obligé qu'on m'impose ces derniers ?

    Que Home ait un intérêt, peut-être, mais il doit être évaluer à l'aulne de ce qui ce fait actuellement, pour qu'il se positionne "correctement".
    Encore une fois, même s'il n'a d'intérêt que pour moi, ça me plait de définir un langage et d'écrire son compilateur. Que je le positionne "correctement" ou pas n'est pas important. Et, sans offense, vous ne savez rien de mon petit langage, vous tirez des conclusions très "tranché" en ayant 1% de l'idée de ce qu'il sera et de son "environnement", car c'est en développement, rien n'est figé.

    Mais faite un test auprès des jeunes de maintenant, les victimes de vos "pédagogues spécialisés", et demander leur d'écrire un programme, dans n'importe quel langage que vous voulez, qui effectue un tri d'une série de 20 chiffres. Je prend cet exemple, car sur mon vieux C64, en 1982, j'ai "redécouvert" quelque choses qui "existait" déjà, le tri à Bulle, et j'ai compris pourquoi il était si lent dès que la série de chiffre a trier était plus grande. Ce n'est que 3 ou 4 ans plus tard que j'ai su que c'était une technique qui avait déjà été découverte (pas de web à l'époque, juste moi et mes 14 ans et 1/2 livres expliquant le BASIC) par d'autres. Savoir que j'avais pu, même si c'était la moins bonne manière de faire un tri, faire a 14 ans dans ma chambre, ce qui avait été trouvé par d'autres m'a rendu assez fier de moi, je vous l'avoue.

    Je suis un "auto-didacte". Pas un "Théoricien", et certainement pas un "Pédagogue spécialisé". Je vous invite à faire ce test. Prenez 10 enfants de 14 ans, et demander leur qu'ils implémentent avec "Scrath" (seul, sans personne pour les guider, sans manuel sur la question, et sans aller copier quelque chose trouvé sur le web), le tris de quelques chiffres, et puis, si certains y arrivent via un tris à bulle, d'expliquer pourquoi il est de plus en plus lent une fois que la série de chiffres augmente. J'ai su le faire à 14 ans en BASIC avec un C64 qui tournait à 1 MHz. Ils devraient en être capable, non ?

    Mais je vous remercie de vos remarques. On peut toujours tirer des informations ou des idées en échangeant, même et peut-être surtout avec des personnes qui ne pensent pas comme vous.

    Avec tout mon respect, BàV et Peace & Love.

  3. #23
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Bonjour
    Citation Envoyé par jo_link_noir Voir le message
    J'ai mal lu .
    Du coup non, y a pas.

    @OuftiBoy:

    ptr + vec, je ne comprends pas l'accumulation. Ni pourquoi un ptr seul à besoin d'être alloué, mais pas un ptr vec. Mais si je comprends bien, vec est builtin, on ne peut pas implémenter vec.add ?
    C'était juste pour montrer qu'on peut mettre ça dans un "Type".
    Sinon, oui, on peut faire ça directelent un vec.

    La syntaxe n'est pas figée, c'est en réflection;

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    ; tile est un type, prédéfinis ou pas.
    
    tile tab1[layer=4, y=50, x=80]   ; déclaration d'un tableau statique 3D.  layer, y, et x sont des "indexeur".
    tile tab2<layer=4, y=50, x=80> ; déclaration d'un tableau similaire en mémoire dynamique.
    
    int8 a=3, b=25, c=40
    tile tab3<layer=a, y=b, x=c>
    
    ; tab1 et tab2 sont des pointeurs, mais non exposés à l'utilisateur
    ; on ne peut accéder à des éléments des tableaux que via les indexeur, qui sont des "ranges", pas besoin de 3 boucles for "imbriquées"
    
    for layer, y, x in tab1:
        ; parcoureras tout les layer, y, x de tab1
    next
    
    for y, x in tab1[layer=0]
        ; parcoureras toutes les lignes et colonnes du layer 0
    next
    
    for y, x in tab2<layer=0>
        ; parcoureras toutes les lignes et colonnes du layer 0
    next
    
    tile une_tile := tab1[layer=0, y=0, x=0] ; assigne la tile qui est en 0,0,0 à une_tile
    Une fois dans l'éditeur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    screen game
    game.print("Hello World", 0, 0)
    screen pourrait être soit un type prédéfini, où être dans une lib incluses automatiquement ou pas.

    Je rejoins bacelar sur la facilité d'utilisation: un pointeur est une notion compliquée pour un débutant, ce n'est pas simple. Plein de langages se débarrassent du problème en ce reposant sur un GC.
    Il y a des pointeurs, nécessaire pour faire des lib ou des type de base.
    Dans les exemples ci-dessus, ils y en a, mais le gamin/programmeur ne le sait même pas.

    On pourrait même avoir simplement:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    game.print("Hello World")
    si game est un screen automatiquement créer.
    il y aura différent mode, de très simple à plus compliqué.

    C'est l'idée.

    Qd je parle de VM, c'est juste un programme qui va interprèté le code binaire, c'est pas une VM comme une VM Linux qui tournerai dans un VM sous Windows.

    Chacuns a son avis, et c'est très bien ainsi.

    Moi je trouve qu'allumer l'ordi, booter sur un TinyCore (un mini linux fonctionnant en RAM), qui peut être sur une simple SD Card ou Clef USB, arrivant le plus rapidement possible à un éditeur intégré dans la VM. Et pouvoir suivre un Manuel qui indique comment afficher un "Hello Word" qlq seconde après le démmarage de l'ordi, être dans un environnement "cloisonné", et ne pas avoir d'obstacles, ni temporellement, ni intellectuement parlant, et avoir un résultat immédiat du "programme" écrit en 1 ligne, c'est une bonne idée. Si vous pensez que non, c'est votre choix que je respecte.

    Ici, le gamin a juste,

    - allumer son ordi
    - attendu qlq secondes
    - taper la ligne montrée en exemple dans le manuel
    - et il a le résultat immédiatement.

    Vous pouvez m'expliquer une manière plus rapide d'arriver à un tel résultat via Python/Pygame ou Scratch. En combien de temps, avec combien d'étapes ? Le gamin de 14 ans étant seul dans sa chambre ? J'aimerais savoir.

    Mais, encore une fois, si j'arrive jusque là tant mieux, sinon j'aurais pris du plaisir a développer tant le Home que la VM du Homeputer.

    BàV, et Peace & Love.

  4. #24
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Bonjour
    Citation Envoyé par mintho carmo Voir le message
    TL;DR : "c'etait mieux avant".
    Je n'ai pas dis cela, mais c'est devotre part un "argument" un peu facile, pour couper cours à toute discution et ne pas répondre à une simple question.

    Comme dans tout domaine, des choses étaient "mieux avant", et d'autres sont "mieux maintenant", ce n'est pas 0 ou 1.
    Ai-je fais du mal à quelqu'un ? Ai-je insulté quelqu'un ? Ai-je critiqué quelque chose ? On peut toujours réflèchir ou c'est interdir ça oui ? Suis nostalgique d'une époque, certainement. J'ai rarement connu quelqu'un qui n'était nostaligique de rien. Suis-je un danger d'essayer de "recréer" une exprécience que j'avais trouvée formidable lorsque j'était plus jeune ? Je ne suis pas venu poser des questions ici pour dénigrer qui que ou quoi que ce soit, j'ai justement demander qu'on critique UN des aspects de mon petit projet, et il me semble que j'ai répondu poliment et en argumentant mes réponses.

    Je ne suis pas ici pour perdre du temps avec des discutions stériles. Chacuns fait ce qu'il veut. Mais ce cite se nomme developpez.net, et est donc un endroit où l'on parle, et discute de programmation.

    Ceci dit, je vous souhaite une bonne fin de semaine, un bon week-end et plein de bonne chose pour la suite.

    BàV, et Peace & Love.

  5. #25
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Je reviens sur ce sujet après longtemps et je n'ai pas (encore) tout lu... Excusez moi pour les éventuelles redites
    Citation Envoyé par OuftiBoy Voir le message
    [...]

    4./ Dans "ma proposition", c'est que la fonction qui a reçu la "propriété", soit "obligée" (par la syntaxe de la fonction), de rendre la propriété à l'appelant.
    Pourquoi devrait il en être ainsi

    Car, en définitive, il y a trois situations possibles:
    1. Soit le propriétaire "actuel" refuse de transférer la propriété, et la fonction qui s'attend à la recevoir ne peut être appelée.
    2. Soit le propriétaire "actuel" accepte de transférer cette propriété, et, du coups, c'est à la fonction l'ayant recue de prendre ses responsabilités, potentiellement sur base de la troisième possibilité
    3. soit le "nouveau propriétaire" n'est en quelque sorte qu'un "intermédiaire" entre deux propriétaire à "plus long terme"

    Il n"y a -- à mon sens -- aucune raison de privilégier une siutation par rapport aux deux autres, car les trois peuvent se présenter.
    5./ Dans la fonction appelée, la syntaxe permet au compilateur de savoir qu'il va devoir rendre la "propriété" à l'appelant, et que donc dans ce cas, il ne fait pas la désallocation automatique en fin du scope de la fonction appelée. Et donc on évite un bug free-after-use.
    A moins qu'il ne doive transmettre cette propriété à un autre élément qui en sera un propriétaire légitime "à plus long terme"...

    6./ D'où l'idée de "forcer" la fonction a rendre la "propriété" à l'appelant. Qui lui fera la déssalocation à la fin de son *scope* (celui où a été faite l'allocation). Ce que ne permet pas de faire Rust. Donc l'apparition de la notion de Borrowing.
    et que fais tu des deux autres possibilités envisagées
    7./ Il n'y a donc pas de notion de "transfert" dans ma "proposition", juste le "Borrowing", le pointeur (où quoi que ce soit d'autre) est "juste prèté" à la fonction qu'on appelle. C'est donc juste "un prêt", et comme c'est un "prêt", la fonction appelée "DOIT" rendre la propriété à l'appelant. Et la syntaxe que je propose permet de le faire.
    Ben, ca, c'est déjà fait ... une référence constante sur un std::unique_ptr ne signifie pas forcément que la ressource ne peut pas être modifiée. Cela signifie juste que l'on ne peut pas demander au pointeur intelligent de libérer la ressource ou d'en prendre une autre en charge.

    Et, au pire, si l'idée de base est que le propriétaire "actuel" doit rester ... propriétaire de la ressource, rien n'empêche de ne transférer que la ressource sous forme d'une référence (constante ou non), pour être sur que la fonction appelée n'essaye pas de la libérer.

    8./ Comme on est "certains" que la fonction appelante "récupérera" ce qu'elle a prêté, via la syntaxe que je propose, on évite automatiquement les bugs **use-after-free**, sans même devoir y penser, là au Rust doit mettre en place le BorrowChecker, car il ne peut pas faire autrement.

    En résumé, la fonction A demande à la fonction B de faire une allocation, en lui "prêtant" le pointeur avec lequel elle peut faire l'allocation. Quand B se termine, elle ne fait pas la désallocation, sachant que si elle le fait, cela créera un bug *free-after-use* au niveau de A, et est "obligée" (c'est même fait d'une manière "transparente") de retourner le pointeur que A lui a donné.
    Et pourquoi, alors, ne pas tout simplement demander à la fonction A de s'occuper de l'allocation, puis s'arranger pour que la ressource transmise à B ne puisse en aucun cas être libérée en la transmettant sous forme de référence
    Si la fonction A prête un pointeur qui pointe déjà vers un bloc, elle ne peut non plus déssalouer ce bloc, pour la même raison qu'elle est obligée de rendre le pointeur à A.
    Déjà, pourquoi devrait elle prêter un pointeur Ne pourrait elle pas donner une référence à la place
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #26
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Bonjour, et merci..
    Merci de soulever ces questions poliment et en avançant des arguments, c'est une démarche contructive que j'apprécie vraiment.
    Je vais tenter de répondre à vos questions, et n'hésitez pas à me contredire. Je profite de votre démarche pour répèter que je ne suis pas un "théoricien", que c'est le "premier langage" que je développe, et également le premier "compilateur". Je suis ne prétend nullement que mon approche est LA bonne approche, et que je suis ouvert à toute critique.

    Ceci étant dit, voici mes réponses:

    Citation Envoyé par koala01 Voir le message
    Je reviens sur ce sujet après longtemps et je n'ai pas (encore) tout lu... Excusez moi pour les éventuelles redites

    Pourquoi devrait il en être ainsi

    Car, en définitive, il y a trois situations possibles:
    1. Soit le propriétaire "actuel" refuse de transférer la propriété, et la fonction qui s'attend à la recevoir ne peut être appelée.
    2. Soit le propriétaire "actuel" accepte de transférer cette propriété, et, du coups, c'est à la fonction l'ayant recue de prendre ses responsabilités, potentiellement sur base de la troisième possibilité
    3. soit le "nouveau propriétaire" n'est en quelque sorte qu'un "intermédiaire" entre deux propriétaire à "plus long terme"

    Il n"y a -- à mon sens -- aucune raison de privilégier une siutation par rapport aux deux autres, car les trois peuvent se présenter.
    Il n'y a qu'un seul et unique "propriétaire", qui est le scope dans lequel a été faites une allocation dynamique, et qui a retourné un pointeur vers la mémoire allouée dynamiquement. Pour assurer qu'il est reste le propriétaire, il ne fait que "prêter" ce pointeur à une autre fonction. Le fait de ne pouvoir que "prêter" est assuré par la syntax du langage. Cette syntaxe fait la distinction entre ce que je nomme les "paramètres d'entrée", qui sont placés entre les () de la fonction appellée, et les "paramètres de sortie", qui se trouvent après l'opérateur '<-' dans la définition de la fonction. Ces paramètres de sortie SONT les variables qui sont assignées au retour de l'appel de la fonction.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    // définition d'une fonction:
    fct do_something(int8 a, b) <- int16 sum, int16 mul:
        sum := a + b
        mul := a * b
    end
    
    int16 r1, r2
    r1, r2 := do_something(10, 6)
    Les variables r1 et r2 sont passée en argument de la fonction après l'opérateur '<-'. sum et mul sont utilisé dans la fonction, toute comme a et b. A la fin de la fonction do_something, sum et mul sont automatiquements retourné, et r1 se voit assigner sum, et r2 se voit assigner mul. En fait sum EST r1 et mul EST r2.

    Quelque soient les paramètres de sortie, et les appels que ferait la fonction do_something, le fonctionnement reste le même.
    Si l'on donne en argument de sortie un pointeur, le fonctionnement reste le même.
    L'appelant reste l'unique propriétaire "éternel", et la désallocation se fera lorsque son scope se terminera.

    Quand une fonction reçoit un pointeur comme paramètre de sortie, le compilateur "sait" qu'il ne doit pas en faire la désallocation, et ce par la syntaxe même du langage.

    le cas 1:

    Citation Envoyé par koala01 Voir le message
    Soit le propriétaire "actuel" refuse de transférer la propriété, et la fonction qui s'attend à la recevoir ne peut être appelée.
    Tout comme une fonction "normal" qui s'attendant à recevoir 2 arguments, créera une erreur si on l'appel avec 1 seul argument. Pour appeler une fonction, il faute respecter sa définition. Il n'y a rien de nouveau ici me semble-t-il ?.

    le cas 2:

    Citation Envoyé par koala01 Voir le message
    Soit le propriétaire "actuel" accepte de transférer cette propriété, et, du coups, c'est à la fonction l'ayant recue de prendre ses responsabilités, potentiellement sur base de la troisième possibilité.
    Le "propriétaire" ne transfert JAMAIS la propriété. Il ne fait que de la prêter, et la syntaxe du langage empêche qu'il en soit autrement.

    Citation Envoyé par koala01 Voir le message
    Soit le "nouveau propriétaire" n'est en quelque sorte qu'un "intermédiaire" entre deux propriétaire à "plus long terme"
    Il n'y a JAMAIS de nouveau propriétaires.

    Citation Envoyé par koala01 Voir le message
    A moins qu'il ne doive transmettre cette propriété à un autre élément qui en sera un propriétaire légitime "à plus long terme"...
    Il ne faut pas confondre la notion "d'utilisateur" et celle de "propriétaire". Un pointeur donné comme paramètre de sortie, peut être utilisé par d'autres fonctions, mais ne peut leur en donner la "propriété", car elle-même n'en a pas la propriété..

    Citation Envoyé par koala01 Voir le message
    et que fais tu des deux autres possibilités envisagées

    Ben, ca, c'est déjà fait ... une référence constante sur un std::unique_ptr ne signifie pas forcément que la ressource ne peut pas être modifiée. Cela signifie juste que l'on ne peut pas demander au pointeur intelligent de libérer la ressource ou d'en prendre une autre en charge.
    C'est un peut la même idée, en effet. Par exemple, une fonction peut allouer un tableau de 10 entiers, via la syntax:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        tableau_alloue_dynamiquement := <int8 x=10, y=10>
        tableau_allouer_statiquement := [int8 x=10, y=10]
    x et y sont des "indexeurs", et l'on peut uniquement passer par eux pour accéder au tableau. Par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
       for x, y in tableau_allouer_dynamiquement:
          ...
       end
    
       for x, y in tableau_allouer_statiquement:
          ...
       end
    Citation Envoyé par koala01 Voir le message
    Et, au pire, si l'idée de base est que le propriétaire "actuel" doit rester ... propriétaire de la ressource, rien n'empêche de ne transférer que la ressource sous forme d'une référence (constante ou non), pour être sur que la fonction appelée n'essaye pas de la libérer.
    C'est ce qui est fait, mais avec un syntaxe nettement plus lègère.

    Citation Envoyé par koala01 Voir le message
    8./ Comme on est "certains" que la fonction appelante "récupérera" ce qu'elle a prêté, via la syntaxe que je propose, on évite automatiquement les bugs **use-after-free**, sans même devoir y penser, là au Rust doit mettre en place le BorrowChecker, car il ne peut pas faire autrement.

    Et pourquoi, alors, ne pas tout simplement demander à la fonction A de s'occuper de l'allocation, puis s'arranger pour que la ressource transmise à B ne puisse en aucun cas être libérée en la transmettant sous forme de référence

    Déjà, pourquoi devrait elle prêter un pointeur Ne pourrait elle pas donner une référence à la place
    C'est exactement ce qui est fait, mais il n'y a que des pointeurs, qui peuvent être "prêtés" à d'autres fonctions, qui ne pourrons pas effectuer de désallocation, car elle ne SERONT jamais propriétaire. "Théoriquement", on pourrait dire qu'un pointeur donner en paramètre de sortie est une "référence".

    Voilà, j'espère avoir répondu à vos questions.

    Tout ceci est en développement, mais commence à se stabiliser petit à petit, et les notions a s'affirmer.

    Je n'ai pas d'ambition pour que ce langage deviennent un langage "mainstream". Mais ce n'est pas uniquement non plus un "travail académique", ce langage n'est qu'une "partie" d'un projet bien plus "large". Mais il est encore trop tôt pour évoquer ce projet, qui avance cependant lui aussi petit à petit...

    Je vous remercie encore, et si vous pointez une "erreur" dans mon raisonnement, je suis tout à fais ouvert à la discution.
    Lorsque tant le langage que son compilateur seront arrivés à une première étape "stable", je compte rendre public tant le langage que le compilateur, le tout expliqué dans le détail.

    BàV et Peace & Love.

  7. #27
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Il n'y a qu'un seul et unique "propriétaire", qui est le scope dans lequel a été faites une allocation dynamique, et qui a retourné un pointeur vers la mémoire allouée dynamiquement.
    Justement, là, vous vous contredites...
    Prenons un exemple (code "C++ like" )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    PtrType * allocate(/* params*/){
        /* some way to dynamically allocate memory */
       return popinter
    }
    void foo(){
        PtrType * ptr = allocate(/* param*/);
    }
    Qui est le propriétaire "légitime" du pointeur dans ce bout de code :question :

    Est-ce la fonciton allocate (qui a effectivement alloué la mémoire) ou est-ce la fonciton foo, qui récupère le pointeur sur la mémoire allouée

    Et, peu importe comment vous allez organiser les choses, même si c'est en introduisant un effet de bord volontaire (ce qui n'est que rarement une bonne solution) sous une forme qui pourrait être proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* here, allocate return true if allocation succeeds, false oftherwise */
    bool allocate(PtrType *& refToPointer, /* other params*/){
        /* some way to dynamically allocate memory  for refToPointer*/
       return success_or_failure;
    }
    
    void foo(){
        PtrType * ptr;
        allocate(ptr, /* other params */);
    }
    Au bout du compte, le propriétaire légitime d'une ressource allouée dynamiquement à partir d'une fonction appelée sera toujours ... la fonction appelante.

    Alors, oui, vous avez (en partie seulement !!!!) raison : il ne peut y avoir qu'un seul propriétaire.

    Cependant, vous oubliez une précision des plus importantes : il ne peut y avoir qu'un seul propriétaire A UN INSTANT DONNE DE L'EXECUTION. Et ce sont ces six mots qui font toute la différence.

    Car, sans ajouter cette précision, ce que vous dites, c'est que c'est la foncton allocate qui devrait être le propriétaire légitime de la ressource allouée dynamiquement, et donc, qu'elle devrait prendre en charge la libération de cette ressource lorsque l'exécution en quitte le scope. C'est à dire ... juste avant de rendre la main à la fonction foo (dans mon exemple). Ce qui rendrait l'allocation dynamique de la ressource ... totalement inutile, vu qu'elle ne pourrait pas être utilisée hors de la fonction qui l'a effectuée (ou en dehors des fonctions que la fonction allocate pourrait appeler).

    Par contre, si vous précisez qu'il ne peut y avoir qu'un seul propriétaire légitime de la ressource A UN INSTANT DONNE DE L'EXECUTION, alors, là, les choses deviennent différentes.

    Parce que cela signifie que la fonction allocate peut "refiler la patate chaude" à la fonction foo (dans mon exemple toujours) pour que ce soit la sortie du scope de cette fonction qui occasionne la libération correcte de la ressource; et, pourquoi pas, que la fonction foo déciide de refiler cette responsabilité soit à une des fonctions qu'elle appelle par la suite, soit à la fonction qui l'a appelée.

    Le "propriétaire" ne transfert JAMAIS la propriété. Il ne fait que de la prêter, et la syntaxe du langage empêche qu'il en soit autrement.
    Justement, comme je viens de vous l'expliquer, c'est sur ce point que vous vous trompez.

    Sans possibilité de transférer la propriété, vous allez vous retrouver à devoir allouer l'ensemble des ressources ... dans votre fonction principale. Car ce sera le seul endroit où vous pourrez être sur que l'ensemble des ressources restera accessible aussi longtemps que vous pourriez en avoir besoin; ce qui risque d'amener un autre problème du "et si j'ai besoin de plus d'espace".

    Il n'y a JAMAIS de nouveau propriétaires.
    Non, vous pouvez imposer le fait qu'il n'y a jamais plus d'un propriétaire A LA FOIS pour une ressource. Ce sera d'ailleurs sans doute une excellente chose.

    Par contre, si vous empêchez le transfert de responsabilité d'un propriétaire vers un autre (étant entendu que "l'ancien" propriétaire n'a alors plus rien à dire quant à la libération de la ressource), vous allez vous limiter dans vos possibilités de manière horrible...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Il ne faut pas confondre la notion "d'utilisateur" et celle de "propriétaire". Un pointeur donné comme paramètre de sortie, peut être utilisé par d'autres fonctions, mais ne peut leur en donner la "propriété", car elle-même n'en a pas la propriété..
    Justement, je suis le premier à insister sur la nécessité de clairement séparer la propriétaire de l'utilisateur.

    Ce dont vous ne semblez pas avoir conscience en terme de ressource allouée dynamiquement, c'est que soit vous devez transférer aux fonctions appelées la ressource elle même sous une forme qui empêchera la fonction appelée de libérer la ressource en question (en C++, nous utiliserons de préférence une référence pour cela), soit c'est carrément le propriétaire en entier que vous devrez transférer, pour que, si vous décidez -- à un moment donné, dans la fonction appelée -- que la ressource peut effectivement être libérée, ce soit effectivement fait en demandant au propriétaire de s'en charger.
    Par exemple, en C++, cela donnerait quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /* Cette fonction utilise une ressource, mais ne peut décider de la libérer */
    void user_no_release( Type /*const */ &  ref){
        /* on peut utiliser la ressource, la modifier si la référence
         * n'est pas constante, mais pas décider de libérer la ressource
         */
    }
    /* cette fonction peut décider de libérer la ressource */
    void possibly_release(std::unique_ptr<Type> & ptr){
        /* some stuff here*/
        if( some_condition)
            ptr.release(); /* parce qu'on a le propriétaire en entier,
                            * on peut lui demander de libérer la ressource
                            */
    }
    void foo(){
         /* le propriétaire de la ressource n'est pas la fonction foo
          * mais bien la donnée unique_pointer
          */
         std::unique_ptr<Type> unique_pointer= std::make_unique<Type>(/* param*/);
         /* on peut appeler une fonction utilisatrice en transférant "ce qui est pointé
          * par le pointeur sous jacent
          */
        user_no_release(*unique_pointer);
        /* on peut aussi transmettre le propriétaire en entier */
       possibly_release(unique_pointer);
       /* seulement, arrivé ici, unique_pointer pointe peut être
        * (si "some_condition" a été vérifiée dans la fonciton appelée)
        * vers une adresse connue pour être invalide (nullptr)
        * Si bien que, si on veut encore accéder au pointeur sous-jacent,
        * il faut commencer par s'assurer de la validité du pointeur
        */
        if(unique_pointer) // si le pointeur est valide
            user_no_release(*unique_pointer) // on peut continuer à l'utiliser
    } // dans tous les cas, comme on quite le scope dans lequel unique_pointer a été déclaré
      // si le pointeur sous-jacent est encore valide, la ressource pointée sera libérée ici
    Par simplicité code tend à valider le fait qu'il n'y a qu'un seul et unique propriétaire, ce qui est tout à fait vrai au demeurant.

    Mais, qu'est ce qui pourrait empêcher ce propriétaire de "se balader" d'une fonction à une autre, de manière à ce que la sortie de scope d'une autre fonction que celle dans laquelle il aura été déclaré provoque effectivement et automatiquement la libération de la ressources

    Après tout, si la fonction foo prenait une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void foo(std::vector<std::unique_ptr<Type> & tab){
        tab.emplace_back(std::make_unique<Type>(/*params*/));
        size_t index= tab.size()-1;
        /* on peut appeler nos deux fonctions */
        user_no_release(*(tab[index]));
        possibly_release(tab[index]);
    }
    la sortie se scope provoquant la libération de la ressource se ferait ... au niveau de la fonction dans laquelle le tableau a été déclaré.

    Mais qui est le propriétaire légitime de la ressource
    Je n'ai pas d'ambition pour que ce langage deviennent un langage "mainstream". Mais ce n'est pas uniquement non plus un "travail académique", ce langage n'est qu'une "partie" d'un projet bien plus "large". Mais il est encore trop tôt pour évoquer ce projet, qui avance cependant lui aussi petit à petit...
    Je comprend bien. Et je dirais que c'est une raison de plus pour prendre le temps de réfléchir correctement à l'outil que vous essayez de créer.

    Car, finalement, un langage de programmation n'est jamais qu'un langage comme le Français : un ensemble de conventions permettant à deux intervenants de se comprendre. La seule différence avec les langages "natifs" étant que l'un des intervenant est ... aussi bête qu'un ordinateur. C'est d'ailleurs la raison pour laquelle l'ordinateur devra utiliser un outil (interpréteur ou compilateur) pour arriver à comprendre ce que les humains auront écrit.

    Seulement, comme n'importe quel outil, il y a "une bonne et une mauvaise manière" de l'utiliser: vous risqueriez gros à essayer de démarrer une tronçonneuse en tenant le porte chaine entre vos jambes. Et vous n'obtiendrez jamais un résultat satisfaisant en essayant d'utiliser un marteau pour enfoncer une vis quelque part.

    Dans le cas présent, non seulement vous voulez créer un nouvel outil, mais en plus, vous voulez définir de nouvelles règles de "bonne utilisation" de l'outil au travers de votre nouveau langage.

    Le problème est que plus vous mettrez de restrictions au niveau du langage, plus vous mettrez de restrictions au niveau des "règles de bonne utilisation de l'outil", plus vous limiterez l'outil dans ses possibilités.

    Et plus vous limiterez l'outil dans ses possibilités, plus vous augmenterez les chances de vous retrouver dans une situation dans laquelle "les règles de bonne utilisation de l'outil" vous empêcheront de faire ce que vous voulez avec l'outil.

    C'est la raison pour laquelle si je comprend l'idée qu'il faille imposer le fait de n'avoir qu'un propriétaire unique pour les ressources dynamiques, je vous conseille très fortement de faire au moins en sorte que ce propriétaire puisse "voyager" d'un scope à l'autre.

    Et, à titre personnel, je vous déconseille très fortement l'idée d'un "paramètre de sortie". Parce que cela implique de compter sur un "effet de bord" qui aura pour effet de rendre la moindre assertion quant à l'état interne de la donnée ayant servi comme telle beaucoup plus difficile, car dépendante des fonctions appelées.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #28
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Oui et non...
    Tout d'abord, merci de votre réponse, clair et argumentée. C'est un plaisir de dialoguer de la sorte.

    Citation Envoyé par koala01 Voir le message
    Justement, là, vous vous contredites...
    Prenons un exemple (code "C++ like" )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    PtrType * allocate(/* params*/){
        /* some way to dynamically allocate memory */
       return popinter
    }
    void foo(){
        PtrType * ptr = allocate(/* param*/);
    }
    Qui est le propriétaire "légitime" du pointeur dans ce bout de code :question :

    Est-ce la fonciton allocate (qui a effectivement alloué la mémoire) ou est-ce la fonciton foo, qui récupère le pointeur sur la mémoire allouée

    Et, peu importe comment vous allez organiser les choses, même si c'est en introduisant un effet de bord volontaire (ce qui n'est que rarement une bonne solution) sous une forme qui pourrait être proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* here, allocate return true if allocation succeeds, false oftherwise */
    bool allocate(PtrType *& refToPointer, /* other params*/){
        /* some way to dynamically allocate memory  for refToPointer*/
       return success_or_failure;
    }
    
    void foo(){
        PtrType * ptr;
        allocate(ptr, /* other params */);
    }
    Au bout du compte, le propriétaire légitime d'une ressource allouée dynamiquement à partir d'une fonction appelée sera toujours ... la fonction appelante.
    Les allocations dynamiques ne seront pas faites via des fonctions, mais via une syntaxe. Le propriétaire de l'allocation sera donc le scope de la fonction qui utilisera cette syntax.

    Par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        int8 abloc[idx=10]            ; alloc static d'un tab. 1D de  10 int8 (.DATA).
        int8 d_bloc<idx=100>      ; alloc dyn tab. 1D de int8, désallouable, de taille fixe 100.
    Citation Envoyé par koala01 Voir le message
    Alors, oui, vous avez (en partie seulement !!!!) raison : il ne peut y avoir qu'un seul propriétaire.

    Cependant, vous oubliez une précision des plus importantes : il ne peut y avoir qu'un seul propriétaire A UN INSTANT DONNE DE L'EXECUTION. Et ce sont ces six mots qui font toute la différence.

    Car, sans ajouter cette précision, ce que vous dites, c'est que c'est la foncton allocate qui devrait être le propriétaire légitime de la ressource allouée dynamiquement, et donc, qu'elle devrait prendre en charge la libération de cette ressource lorsque l'exécution en quitte le scope. C'est à dire ... juste avant de rendre la main à la fonction foo (dans mon exemple). Ce qui rendrait l'allocation dynamique de la ressource ... totalement inutile, vu qu'elle ne pourrait pas être utilisée hors de la fonction qui l'a effectuée (ou en dehors des fonctions que la fonction allocate pourrait appeler).
    Comme dit plus haut, les allocations dynamiques ne se feront pas via des fonctions du genre alloc(), le problème que vous soulevez ne pourra donc jamais se produire me semble-t-il.

    Citation Envoyé par koala01 Voir le message
    Par contre, si vous précisez qu'il ne peut y avoir qu'un seul propriétaire légitime de la ressource A UN INSTANT DONNE DE L'EXECUTION, alors, là, les choses deviennent différentes.

    Parce que cela signifie que la fonction allocate peut "refiler la patate chaude" à la fonction foo (dans mon exemple toujours) pour que ce soit la sortie du scope de cette fonction qui occasionne la libération correcte de la ressource; et, pourquoi pas, que la fonction foo déciide de refiler cette responsabilité soit à une des fonctions qu'elle appelle par la suite, soit à la fonction qui l'a appelée.
    Oui, il n'y a qu'un seul propriétaire, toujours, pas juste "A un instant donné".

    Citation Envoyé par koala01 Voir le message
    Justement, comme je viens de vous l'expliquer, c'est sur ce point que vous vous trompez.

    Sans possibilité de transférer la propriété, vous allez vous retrouver à devoir allouer l'ensemble des ressources ... dans votre fonction principale. Car ce sera le seul endroit où vous pourrez être sur que l'ensemble des ressources restera accessible aussi longtemps que vous pourriez en avoir besoin; ce qui risque d'amener un autre problème du "et si j'ai besoin de plus d'espace".
    Excusez-moi, je n'avais pas précisé que les allocations ne sont pas faites via des fonctions, mais via une syntaxe du langage, et des allocations pourront se faire dans n'importe quelle fonction, mais c'est cette fonction qui en sera l'unique et éternel propriétaire.

    Citation Envoyé par koala01 Voir le message
    Non, vous pouvez imposer le fait qu'il n'y a jamais plus d'un propriétaire A LA FOIS pour une ressource. Ce sera d'ailleurs sans doute une excellente chose.
    Ce sera le cas, mais le propriétaire restera toujours le scope de la fonction qui a fait l'allocation (qui elle se fait sans utiliser de fonctions).

    Citation Envoyé par koala01 Voir le message
    Par contre, si vous empêchez le transfert de responsabilité d'un propriétaire vers un autre (étant entendu que "l'ancien" propriétaire n'a alors plus rien à dire quant à la libération de la ressource), vous allez vous limiter dans vos possibilités de manière horrible...
    Il n'a y pas de transfert de propriété, mais un "prêt" uniquement. Un "prêt" qui via la syntaxe, est obligatoirement "remboursé" (rendu).

    Citation Envoyé par koala01 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Il ne faut pas confondre la notion "d'utilisateur" et celle de "propriétaire". Un pointeur donné comme paramètre de sortie, peut être utilisé par d'autres fonctions, mais ne peut leur en donner la "propriété", car elle-même n'en a pas la propriété..
    Justement, je suis le premier à insister sur la nécessité de clairement séparer la propriétaire de l'utilisateur.
    Je suis parfaitement d'accord avec vous sur ce point.

    Citation Envoyé par koala01 Voir le message
    Ce dont vous ne semblez pas avoir conscience en terme de ressource allouée dynamiquement, c'est que soit vous devez transférer aux fonctions appelées la ressource elle même sous une forme qui empêchera la fonction appelée de libérer la ressource en question (en C++, nous utiliserons de préférence une référence pour cela), soit c'est carrément le propriétaire en entier que vous devrez transférer, pour que, si vous décidez -- à un moment donné, dans la fonction appelée -- que la ressource peut effectivement être libérée, ce soit effectivement fait en demandant au propriétaire de s'en charger.

    Par exemple, en C++, cela donnerait quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /* Cette fonction utilise une ressource, mais ne peut décider de la libérer */
    void user_no_release( Type /*const */ &  ref){
        /* on peut utiliser la ressource, la modifier si la référence
         * n'est pas constante, mais pas décider de libérer la ressource
         */
    }
    /* cette fonction peut décider de libérer la ressource */
    void possibly_release(std::unique_ptr<Type> & ptr){
        /* some stuff here*/
        if( some_condition)
            ptr.release(); /* parce qu'on a le propriétaire en entier,
                            * on peut lui demander de libérer la ressource
                            */
    }
    void foo(){
         /* le propriétaire de la ressource n'est pas la fonction foo
          * mais bien la donnée unique_pointer
          */
         std::unique_ptr<Type> unique_pointer= std::make_unique<Type>(/* param*/);
         /* on peut appeler une fonction utilisatrice en transférant "ce qui est pointé
          * par le pointeur sous jacent
          */
        user_no_release(*unique_pointer);
        /* on peut aussi transmettre le propriétaire en entier */
       possibly_release(unique_pointer);
       /* seulement, arrivé ici, unique_pointer pointe peut être
        * (si "some_condition" a été vérifiée dans la fonciton appelée)
        * vers une adresse connue pour être invalide (nullptr)
        * Si bien que, si on veut encore accéder au pointeur sous-jacent,
        * il faut commencer par s'assurer de la validité du pointeur
        */
        if(unique_pointer) // si le pointeur est valide
            user_no_release(*unique_pointer) // on peut continuer à l'utiliser
    } // dans tous les cas, comme on quite le scope dans lequel unique_pointer a été déclaré
      // si le pointeur sous-jacent est encore valide, la ressource pointée sera libérée ici
    Par simplicité code tend à valider le fait qu'il n'y a qu'un seul et unique propriétaire, ce qui est tout à fait vrai au demeurant.
    On peut donner cette ressource à une autre fonction sans soucis, elle en sera l'utilisatrice **unique**, et pourra par exemple ajouter des éléments à un tableau, mais sans en devenir le propriétaire. Ce qui sera ajouté au tableau ne sera évidemment pas désalloué en quittant la fonction qui a fait ces ajouts dans le tableau qu'elle utilise. Le tableau sera étendu, les "ajout" étant fait dans le tableau seront sous la responsabilité du propriétaire unique du tableau. Un "ajout" n'est pas un "transfert", car "l'ajout" sera fait dans le tableau, son propriétaire les désallouera lorsque son scope se terminera. Ces "ajouts" seront également fait via la syntax, sans appel de fonctions.

    Citation Envoyé par koala01 Voir le message
    Mais, qu'est ce qui pourrait empêcher ce propriétaire de "se balader" d'une fonction à une autre, de manière à ce que la sortie de scope d'une autre fonction que celle dans laquelle il aura été déclaré provoque effectivement et automatiquement la libération de la ressources

    Après tout, si la fonction foo prenait une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void foo(std::vector<std::unique_ptr<Type> & tab){
        tab.emplace_back(std::make_unique<Type>(/*params*/));
        size_t index= tab.size()-1;
        /* on peut appeler nos deux fonctions */
        user_no_release(*(tab[index]));
        possibly_release(tab[index]);
    }
    la sortie se scope provoquant la libération de la ressource se ferait ... au niveau de la fonction dans laquelle le tableau a été déclaré.

    Mais qui est le propriétaire légitime de la ressource
    Je comprend bien. Et je dirais que c'est une raison de plus pour prendre le temps de réfléchir correctement à l'outil que vous essayez de créer.
    Le propriétaire d'un tableau sera le propriétaire des éléments qu'on lui ajoute car ces ajouts seront fait via le tableau lui-même, qui a été prêté.

    Citation Envoyé par koala01 Voir le message
    Car, finalement, un langage de programmation n'est jamais qu'un langage comme le Français : un ensemble de conventions permettant à deux intervenants de se comprendre. La seule différence avec les langages "natifs" étant que l'un des intervenant est ... aussi bête qu'un ordinateur. C'est d'ailleurs la raison pour laquelle l'ordinateur devra utiliser un outil (interpréteur ou compilateur) pour arriver à comprendre ce que les humains auront écrit.
    Je suis parfaitement d'accord avec vous.

    Citation Envoyé par koala01 Voir le message
    Seulement, comme n'importe quel outil, il y a "une bonne et une mauvaise manière" de l'utiliser: vous risqueriez gros à essayer de démarrer une tronçonneuse en tenant le porte chaine entre vos jambes. Et vous n'obtiendrez jamais un résultat satisfaisant en essayant d'utiliser un marteau pour enfoncer une vis quelque part.
    Heureusement, je ne suis pas un manuel ;-)

    Citation Envoyé par koala01 Voir le message
    Dans le cas présent, non seulement vous voulez créer un nouvel outil, mais en plus, vous voulez définir de nouvelles règles de "bonne utilisation" de l'outil au travers de votre nouveau langage.

    Le problème est que plus vous mettrez de restrictions au niveau du langage, plus vous mettrez de restrictions au niveau des "règles de bonne utilisation de l'outil", plus vous limiterez l'outil dans ses possibilités.

    Et plus vous limiterez l'outil dans ses possibilités, plus vous augmenterez les chances de vous retrouver dans une situation dans laquelle "les règles de bonne utilisation de l'outil" vous empêcheront de faire ce que vous voulez avec l'outil.
    Je comprend parfaitement votre point de vue, mais l'idée n'est vraiment pas de "mettre des verrous partout", au contraire. Mais oui, j'essaye que ce langage soit "carré", sans ambiguïté, facile d'utilisation, et au code source très lisible.

    Sauf erreur de ma part, je ne vois pas où j'impose des "limites", mais oui, j'essaye de "penser" autrement. Je ne prétend pas définir de nouvelles règles de "bonne utilisation". J'essaye que mon langage puisse intégrer de "bonnes pratiques", limitant les erreurs qu'un langage comme le C permet de faire.

    Le tout avec une syntax simple, légère, et facilement compréhensible. Je n'arriverais peut-être pas à mon objectif, je vais peut-être tomber sur des cas "non gérables", mais jusqu'à présent, je n'en ai pas encore rencontré. Si je rencontre ces soucis, et bien je penserais à d'autres moyens. Je n'ai pas la prétention de me mettre en concurrence avec d'autres développeurs de compilateur, ou de remplacer des "théoriciens" bien plus compétant que moi. Je reste très humble concernant ce que je tente de faire.

    Citation Envoyé par koala01 Voir le message
    C'est la raison pour laquelle si je comprend l'idée qu'il faille imposer le fait de n'avoir qu'un propriétaire unique pour les ressources dynamiques, je vous conseille très fortement de faire au moins en sorte que ce propriétaire puisse "voyager" d'un scope à l'autre.
    Le "propriétaire" d'une ressource peut la laisser voyager, la laisser être utilisée (mais pas la désallouer) dans d'autres scopes, tout en restant le propriétaire. Si vous donnez les clefs de votre voiture à un ami pour quelque raison que ce soit, il est utilisateur de votre voiture, mais vous en rester le propriétaire, et il devra vous rendre les clefs.

    Le fait que le scope de la fonction reste propriétaire de ce qu'elle a alloué, a pour but premier d'empêcher toute une série de bugs, sans employer des méthodes comme celles utilisées par Rust ou Ada (par exemple, et j'ajoute que je n'ai rien contre ces 2 langages). Le concept d'ownership a des limites, d'ou l'utilisation des références non mutables et des shared références mutable, qui nécessitent l'utilisation d'un "Borrow Checker" pour surveiller tout cela. J'essaye (sans prétendre que j'y arriverais) d'obtenir le même résultat de code "safe & secure", mais plus simplement, avec une autre approche.

    En fait, la notion même de "propriétaire" n'a pas besoin d'être évoquée pour utiliser le langage. Je n'ai d'ailleurs entendu parler de cette notion de "propriétaire" que via Ada & Rust. C'est une notion qui, me semble-t-il, a été développée pour empêcher des **bugs** souvent rencontrés si la gestion de la mémoire dynamique est faite "à la main" (comme en C via alloc(), malloc(), ...).

    Citation Envoyé par koala01 Voir le message
    Et, à titre personnel, je vous déconseille très fortement l'idée d'un "paramètre de sortie". Parce que cela implique de compter sur un "effet de bord" qui aura pour effet de rendre la moindre assertion quant à l'état interne de la donnée ayant servi comme telle beaucoup plus difficile, car dépendante des fonctions appelées.
    Là, j'avoue que je ne comprend pas bien ce que vous voulez dire par "effet de bord". Un "paramètre de sortie" est géré comme un autre argument d'une fonction. Il permet simplement de faire une différence "sémantique" entre 2 types des paramètres, ceux d'entrées qui sont entre () et 'constant' par défaut, et les valeurs que doit retourner une fonction, qui sont 'variables' par défaut. Au niveau du compilateur, ils sont passé via la stack comme les autres arguments, tout comme en POO un argument est passé comme référence à un objet particulier. En python, cela est "explicite", via le 'self' qui est le premier argument de chaque méthode. En C++, c'est le 'this', qui est lui implicite.

    Le fait que ce soit les variables qui sont assignés soit automatiquement les "paramètres de sortie" ne pose pas de limite. Si on passait ces arguments de manière classique, via deux autres arguments, un appel de cette fonction devrait comporter ces arguement lors de l'appel, sinon le compilateur émettra une erreur. Dans mon langage, si une fonction retourne "plusieurs valeurs", il faut qu'il y ai des variables, du type correct, pour "récupérer" ces valeurs.

    Je vous remercie encore du temps que vous passez à "mettre à l'épreuve" les idées que je tente d'implémenter dans mon langage. J'espère avoir répondu à vos questions, mais si ce n'est pas le cas, ou si ces réponses vous semble "fausses/mauvaises/érronées", je serais très heureux de poursuivre cette discution. De même, si je n'ai pas moi-même compris une de vos réponses, veuillez m'en excuser, et n'hésitez pas si vous le désirez, a reprendre et préciser ou expliquer autrement ce que je n'aurais pas bien compris.

    Je ne demande qu'a apprendre ;-)

    Encore merci.
    BàV et Peace & Love.

  9. #29
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Comme dit plus haut, les allocations dynamiques ne se feront pas via des fonctions du genre alloc(), le problème que vous soulevez ne pourra donc jamais se produire me semble-t-il.
    Ce que vous ne semlez pas comprendre, c'est, que l'allocation dynamique soit effectuée au travers d'une fonciton comme malloc, d'un opérateur comme new ou de n'importe quel autre moyen syntaxique mis à votre disposition, elle s'effectuera forcément à l'intérieur d'une fonction, autrement, vous vous obligez à effectuer l'allocation sur des variables globales.

    Donc, par définition, si vous dites que la fonciton dans laquelle l'allocation dynamique représente le scope de validité de cette allocation, la ressource devra être libérrée, au plus tard, au moment où vous quitterez le scope de cette fonction.
    Oui, il n'y a qu'un seul propriétaire, toujours, pas juste "A un instant donné".
    Justement, non.

    On peut ne pas accepter d'avoir deux propriétaires de la même ressource en même temps -- même si, sous certains aspects, cela peut rendre les choses bien plus compliquées -- mais cela n'implique pas forcément que le propriétaire de la ressource ne puisse absolument pas changer au fil du temps.

    C'est un peu comme si vous achetiez une voiture. Vous en êtes le propriétaire légitime pendant plusieurs années, puis, pour une raison quelconque, vous décidez d'en changer alors qu'elle fonctionne parfaitement. Vous décidez donc de la revendre à quelqu'un d'autre (en occasion), afin de pouvoir acheter la nouvelle, dont vous deviendrez le propriétaire.

    Si vous ne vous donnez pas la possibilité d'effectuer un tel "transfert de propriété", tout votre design, toute votre organisation des données doit être envisagée de manière totalement différente, avec des difficultés bien plus importantes de mise en oeuvre.

    Je ne dit pas qu'il est impossible de concevoir vos projets de la sorte, je dis juste que cette manière de concevoir vos projets apporte beaucoup plus de complexité qui aurait pu (aurait du pouvoir) être évitée.

    Pour citer Einstein lui-même, il faut
    Rendre les choses aussi complexes que nécessaires, mais guère plus
    Or, en empêchant le transfert de propriété, vous rendez les choses ... beaucoup plus complexes que nécessaire. Et, finalement, pour un bénéfice beaucoup trop limitée par rapport à la complexité ajoutée.

    Excusez-moi, je n'avais pas précisé que les allocations ne sont pas faites via des fonctions, mais via une syntaxe du langage, et des allocations pourront se faire dans n'importe quelle fonction, mais c'est cette fonction qui en sera l'unique et éternel propriétaire.
    Là non plus, cela ne marche pas.

    Car cela signifie que la seule possibilité d'accéder à la ressource allouée dynamiquement (à l'exception de la fonction dans laquelle l'allocation s'effectue) passe par l'appel, depuis la fonction ayant alloué la ressource, de fonctions tierses.

    Mais que se passe-t-il alors si la fonction qui a appelé la fonciton s'occupant d'allouer la ressource souhaite profiter de cette ressource Peu importe que la fonction appelante récupère cette ressource au travers d'une valeur de retour ou d'un "paramètre de sortie", si la ressource est -- effectivement -- libérée au moment où l'exécution quitte le scope de la fonction "allocative", la ressource n'est ... tout simplement plus disponible pour la fonction appelante.

    Il n'a y pas de transfert de propriété, mais un "prêt" uniquement. Un "prêt" qui via la syntaxe, est obligatoirement "remboursé" (rendu).
    Le problème, c'est que vous vous limitez alors à une logique du genre (code C++ style) de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void foo(){
        Type res = allocated_ressource;
        fonction_pret_1(res);
        fonction_tret_2(res);
        /* il pourrait y avoir des tests, des boucles et tout ce que l'on veut */
    }  // res est libéré car sortie de scope
    Et j'en reviens à ma question: que se passe-t-il si la fonction qui appelle foo s'attend à récupérer la ressource Si vous ne pouvez pas garantir que la fonction appelante sera en mesure de récupérér une ressource valide et, par définitive, d'en devenir le propriétaire légitime, tout votre design devra être adapté en conséquence... Avec une complexité bien supérieure à ce que vous permettrait le fait d'accepter le transfert de propriété.

    Ce dont vous ne semblez pas avoir conscience en terme de ressource allouée dynamiquement, c'est que soit vous devez transférer aux fonctions appelées la ressource elle même sous une forme qui empêchera la fonction appelée de libérer la ressource en question (en C++, nous utiliserons de préférence une référence pour cela), soit c'est carrément le propriétaire en entier que vous devrez transférer, pour que, si vous décidez -- à un moment donné, dans la fonction appelée -- que la ressource peut effectivement être libérée, ce soit effectivement fait en demandant au propriétaire de s'en charger.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /* Cette fonction utilise une ressource, mais ne peut décider de la libérer */
    void user_no_release( Type /*const */ &  ref){
        /* on peut utiliser la ressource, la modifier si la référence
         * n'est pas constante, mais pas décider de libérer la ressource
         */
    }
    /* cette fonction peut décider de libérer la ressource */
    void possibly_release(std::unique_ptr<Type> & ptr){
        /* some stuff here*/
        if( some_condition)
            ptr.release(); /* parce qu'on a le propriétaire en entier,
                            * on peut lui demander de libérer la ressource
                            */
    }
    void foo(){
         /* le propriétaire de la ressource n'est pas la fonction foo
          * mais bien la donnée unique_pointer
          */
         std::unique_ptr<Type> unique_pointer= std::make_unique<Type>(/* param*/);
         /* on peut appeler une fonction utilisatrice en transférant "ce qui est pointé
          * par le pointeur sous jacent
          */
        user_no_release(*unique_pointer);
        /* on peut aussi transmettre le propriétaire en entier */
       possibly_release(unique_pointer);
       /* seulement, arrivé ici, unique_pointer pointe peut être
        * (si "some_condition" a été vérifiée dans la fonciton appelée)
        * vers une adresse connue pour être invalide (nullptr)
        * Si bien que, si on veut encore accéder au pointeur sous-jacent,
        * il faut commencer par s'assurer de la validité du pointeur
        */
        if(unique_pointer) // si le pointeur est valide
            user_no_release(*unique_pointer) // on peut continuer à l'utiliser
    } // dans tous les cas, comme on quite le scope dans lequel unique_pointer a été déclaré
      // si le pointeur sous-jacent est encore valide, la ressource pointée sera libérée ici
    Par simplicité code tend à valider le fait qu'il n'y a qu'un seul et unique propriétaire, ce qui est tout à fait vrai au demeurant.
    On peut donner cette ressource à une autre fonction sans soucis, elle en sera l'utilisatrice **unique**, et pourra par exemple ajouter des éléments à un tableau, mais sans en devenir le propriétaire.
    uniquement pour les fonction qui seraient appelée -- de manière directe ou indirecte -- par foo.

    La question reste malgré tout la même: qu'en est il de la fonction qui fera appel à foo, en particulier si elle souhaite récupérer la ressource allouée par foo

    Dites moi que vous ne voulez pas que la fonction qui appelle foo puisse récupérer la ressource, que vous comprenez les défis auxquels cette décision vous fera faire face, je vous foutrai la paix, et je vous laisserai vous dépêtrer avec vos problèmes (quitte à essayer de vous aider à vous en sortir).

    Ce qui sera ajouté au tableau ne sera évidemment pas désalloué en quittant la fonction qui a fait ces ajouts dans le tableau qu'elle utilise.
    C'est justement là le problème :pour que la ressource allouée dans votre fonction et ajoutée à votre tableau ne soit libérée que lorsque le tableau est détruit, il faut ... transférer la propriété de la ressource au tableau.

    Autrement, c'est la fonction qui s'occupe de l'allocation de la ressource qui, en tant que propriétaire, doit s'assurer de la libérer correctement.

    Cette phrase à elle seule contredit tout ce que vous dite concernant l'impossibilité de transférer la propriété et valide tout ce que moi je peux dire concernant l'absolue nécessité d'autoriser le transfert de propriété.

    Le tableau sera étendu, les "ajout" étant fait dans le tableau seront sous la responsabilité du propriétaire unique du tableau.
    Même pas... la responsabilité de libération des éléments du tableau doit échoire ... au tableau lui-même, pas au propriétaire du tableau.

    Car, autrement, cela voudrait dire que le seul moyen de supprimer un élément et de s'assurer que la ressource sera correctement libérée serait de le faire ... dans la fonction dans laquelle le tableau existe.

    Un "ajout" n'est pas un "transfert", car "l'ajout" sera fait dans le tableau, son propriétaire les désallouera lorsque son scope se terminera. Ces "ajouts" seront également fait via la syntax, sans appel de fonctions.
    Justement, non...
    L'ajout d'un élément au tableau est un transfert de propriété, de la fonction qui crée l'élément vers le tableau qui le contient.
    Je comprend parfaitement votre point de vue, mais l'idée n'est vraiment pas de "mettre des verrous partout", au contraire.
    Pourtant, en décidant qu'une fonciton ne peut pas transmettre la propriété de la ressource qu'elle alloue à la fonction qui l'a appelée, vous placez un énorme verrou qu'il vous sera difficile de contourner
    Mais oui, j'essaye que ce langage soit "carré", sans ambiguïté, facile d'utilisation, et au code source très lisible.
    Et c'est tout à votre honneur!

    Le point est que, en l'état, votre réflexion me semble "manquer de profondeur", sans doute à cause d'un manque d'expérience, car vous n'envisagez pas un cas qui se présentera bien plus souvent que vous ne semblez le croire.
    Je ne prétend pas définir de nouvelles règles de "bonne utilisation".
    C'est pourtant ce que vous faites en essayant de définir votre propre langage.

    C'est ce que fait n'importe qui en essayant de définir un langage : définir un ensemble de règles permettant à un outil de comprendre ce que l'on attend de lui
    J'essaye que mon langage puisse intégrer de "bonnes pratiques", limitant les erreurs qu'un langage comme le C permet de faire.
    Et c'est une excellente chose.

    Cela ne doit juste pas se faire en mettant au placard des pratiques au sujet desquelles cinquante ans d'évolution dans les langages ont clairement démontré qu'elles étaient incontournables.


    Le "propriétaire" d'une ressource peut la laisser voyager, la laisser être utilisée (mais pas la désallouer) dans d'autres scopes, tout en restant le propriétaire. Si vous donnez les clefs de votre voiture à un ami pour quelque raison que ce soit, il est utilisateur de votre voiture, mais vous en rester le propriétaire, et il devra vous rendre les clefs.
    C'est vrai lorsque vous êtes au niveau d'une fonction appelante...

    Mais une fonction ne vaut que si elle est appelée. La question est donc toujours la même : que se passe-t-il si la fonction est appelée par une fonction qui souhaite récupérer la ressource allouée
    Le fait que le scope de la fonction reste propriétaire de ce qu'elle a alloué, a pour but premier d'empêcher toute une série de bugs,
    L'initiative est louable, certes, mais elle pose une série de problème qui seront bien plus difficile à prendre en compte et à résoudre que le simple fait d'accepter l'idée que la propriété d'une ressource allouée par une fonction puisse être prise en charge par (et donc transférée à) la fonction ayant appelé la fonction responsable de l'allocation de la ressource.
    Là, j'avoue que je ne comprend pas bien ce que vous voulez dire par "effet de bord".
    On parle d'effet de bord lorsqu'une donnée issue de la fonction appelante est modifiée par la fonction appelée.
    Un "paramètre de sortie" est géré comme un autre argument d'une fonction.
    non, parce que le paramètre est destiné à être modifié par la fonction appelée, et les modifications apportées à ce paramètre seront répercutées sur la donnée fournie par la fonction appelante.

    Là, j'avoue que je ne comprend pas bien ce que vous voulez dire par "effet de bord". Un "paramètre de sortie" est géré comme un autre argument d'une fonction. Il permet simplement de faire une différence "sémantique" entre 2 types des paramètres, ceux d'entrées qui sont entre () et 'constant' par défaut, et les valeurs que doit retourner une fonction, qui sont 'variables' par défaut.
    Le fait est que, si une fonction a besoin d'une donnée externe (comprenez : dont elle ne peut définir par elle même la valeur à l'exécution), il vaut mieux que cette donnée soit immuable ("paramètre d'entrée" pour reprendre vos propres termes), mais que si elle doit rendre une donnée accessible à la fonction qui l'a appelée, il vaut mieux le faire en lui permettant de renvoyer cette donnée plutôt qu'en décidant de modifier une donnée fournie par la fonction appelante (vos "paramètres de sortie"), justement, à cause de l'effet de bord que le fait de modifier cette donnée fournie par la fonction appelante implique.
    tout comme en POO un argument est passé comme référence à un objet particulier. En python, cela est "explicite", via le 'self' qui est le premier argument de chaque méthode. En C++, c'est le 'this', qui est lui implicite.
    Ah, OK... Je vois ce qui vous intrigue...

    Le fait est que this ou self ne sont pas "vraiment" des paramètres de sortie. Enfin, si, techniquement parlant, ce sont bel et bien des paramètres de sorite. Seulement, on entre là dans le domaine des fonctions membres. Des fonctions qui n'existent que parce qu'il existe un type de donnée à partir duquel les appeler, et qui ne peuvent être appelées qu'au départ d'une instance du type de donnée en question.

    Lorsque la fonction membre modifier les données internes de l'objet à partir duquel elle est appelée, il y a bien "effet de bord", cependant, il reste malgré tout limité, et s'il y avait moyen de l'éviter (entre autres, pour les types de données ayant sémantique de valeur), ce serait pas plus mal.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #30
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    103
    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 : 103
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Il n'a y pas de transfert de propriété, mais un "prêt" uniquement. Un "prêt" qui via la syntaxe, est obligatoirement "remboursé" (rendu).
    Si vous creusez l'idée, cela vous amène doucement à une sémantique de prêt à la Rust.
    Là vous vous en passez parce que vos exemples sont essentiellement mono-thread et non-concurrent. -> Je prête ma propriété et j’attends le retour de fonction pour récupérer ma propriété.
    Avec votre sémantique, je ne suis même pas sûr que vous puissiez prêter ce que vous avez emprunté.

    Mais considérons maintenant des exemples avec appels concurrents ou multithreads, alors le processus appelant peut continuer de s'exécuter: quelles sont les règles d'utilisation des données déjà prêtées?
    Comment faites vous pour prêter à plusieurs processus? (ce qui peut être utile pour partager des données entres plusieurs threads)

  11. #31
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    103
    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 : 103
    Par défaut
    Du coup, cela m'amène à me replonger dans d'anciens messages.
    Citation Envoyé par OuftiBoy Voir le message
    Par rapport à Rust, la fonction appelée NE PEUT PAS ne pas retourné UN PARAMETRE de sortie.
    C'est le problème de Rust, car comme il ne sait pas OBLIGER qu'on retourne à la fonction appelante la référence ou le pointeur (la troisième régle de l'Ownership et 'remplacée' par la notion de Borrowing), qui implique un 'BorrowChecker'.
    Votre affirmation n'est pas claire du tout pour moi. Qu'est-ce que vous entendez par là.
    Si je reprends votre exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // définition d'une fonction:
    fct do_something(int8 a, b) <- int16 sum, int16 mul:
        sum := a + b
        mul := a * b
    end
     
    int16 r1, r2
    r1, r2 := do_something(10, 6)
    Cela ressemble à un code Rust de la forme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // définition d'une fonction:
    fn do_something(a: &i16, b: &i16, sum: &mut i16, mul: &mut i16) {
        *sum = a + b;
        *mul = a * b;
    }
     
    let [mut r1,mut r2] = [0,0];
    do_something(&10, &6, &mut r1, &mut r2);
    En d'autre termes, c'est comme si vous utilisiez des références non mutables et mutables, mais de manière implicite par la structuration de vos fonctions.

  12. #32
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Bonjour et merci
    Merci de me consacrer du temps et de soulever des problèmes (présent où a venir) que je ne vois pas.

    Citation Envoyé par koala01 Voir le message
    Donc, par définition, si vous dites que la fonciton dans laquelle l'allocation dynamique représente le scope de validité de cette allocation, la ressource devra être libérrée, au plus tard, au moment où vous quitterez le scope de cette fonction.
    Je ne dis pas que c'est le "scope de validité", j'impose juste que c'est ce scope et lui seul, qui pourra effectuer la désallocation.

    Citation Envoyé par koala01 Voir le message
    Justement, non.

    On peut ne pas accepter d'avoir deux propriétaires de la même ressource en même temps -- même si, sous certains aspects, cela peut rendre les choses bien plus compliquées -- mais cela n'implique pas forcément que le propriétaire de la ressource ne puisse absolument pas changer au fil du temps.

    Si vous ne vous donnez pas la possibilité d'effectuer un tel "transfert de propriété", tout votre design, toute votre organisation des données doit être envisagée de manière totalement différente, avec des difficultés bien plus importantes de mise en oeuvre.

    Je ne dit pas qu'il est impossible de concevoir vos projets de la sorte, je dis juste que cette manière de concevoir vos projets apporte beaucoup plus de complexité qui aurait pu (aurait du pouvoir) être évitée.
    Je comprend ce que vous dites, le problème, c'est qu'apparemment je ne vois pas quelles seraient ces complexités.

    Je n'ai pas encore réalisé de "grand" programme avec ce langage, et c'est peut-être pour cela que je vois pas ces complexcités, ni les difficultés plus importante de mise en oeuvre que vous évoquez.

    Actuellement, le code du compilateur en lui-même reste d'une extrème limpidité. Il comporte déjà cependant toutes les phases nécessires, le lexer, le parser, le checker, la production de code intermédiaire (IRC), l'optimisation de ce code, un linker, et la production de code assembleur. Je dois encore y intégrer un "static analyzer", qui pourrait trouvé des erreurs que le "Checker" (qui s'occupe notamment du respect du typage). Toutes ces parties sont appelée succesivement les une après les autres, le lexer fournit une liste de tokens, le parser utilise ces tokens et produit un AST, cet AST est analysé par le checker qui lui produit la Table Des Symboles, le générateur de code IRC utilise la Table Des Symboles et l'AST produit par le parser pour générer le code intermédiaire, qui est lui-même transformé en code assembleur (dont je n'ai pas encore effectuer la "binarisation").

    Actuellement, le langage permet la déclaration de structures, de nouveau types, d'avoir un approche utilisant la POO, d'effectuer des appels de fonctions, de méthode. L'assignation est bien sur également disponible, tout comme l'utilisation de librairires (écritent dans le langage), permet d'utiliser un if/elif.else, d'intancier et d'utiliser les types créé par le développeur, d'effectuer des calculs en respectant la précédence des opérateur (avec ou sans ()).

    C'est un compilateur de type LL(1), dont le code est d'environs 1500 lignes python (hors commentaire).

    Citation Envoyé par koala01 Voir le message
    Pour citer Einstein lui-même, il faut Rendre les choses aussi complexes que nécessaires, mais guère plus.
    Je suis 100% d'accord avec vous. J'ai même une obsession pour la simplicité

    Citation Envoyé par koala01 Voir le message
    Or, en empêchant le transfert de propriété, vous rendez les choses ... beaucoup plus complexes que nécessaire. Et, finalement, pour un bénéfice beaucoup trop limitée par rapport à la complexité ajoutée.
    Veuillez m'excuser, mais je ne vois (actuellement) pas de complexité, ni dans le code du compilateur, ni dans l'utilisation du langage. Le bénéfice (éviter des désallocations) me semble important. Tout est fait et vérifié par le compilateur très simplement.

    Citation Envoyé par koala01 Voir le message
    Cela signifie que la seule possibilité d'accéder à la ressource allouée dynamiquement (à l'exception de la fonction dans laquelle l'allocation s'effectue) passe par l'appel, depuis la fonction ayant alloué la ressource, de fonctions tierses.

    Mais que se passe-t-il alors si la fonction qui a appelé la fonciton s'occupant d'allouer la ressource souhaite profiter de cette ressource Peu importe que la fonction appelante récupère cette ressource au travers d'une valeur de retour ou d'un "paramètre de sortie", si la ressource est -- effectivement -- libérée au moment où l'exécution quitte le scope de la fonction "allocative", la ressource n'est ... tout simplement plus disponible pour la fonction appelante.
    Je pense que l'on ne se comprend pas sur ce point. Si une fonction A appel une fonction B (qui fait l'allocation), elle récupére le "pointeur" via un "paramètre de retour", et peut l'utiliser à sa guise. A la fin de la fonction B, rien ne sera désalloué, car le compilateur sait (puisse qu'il s'agit de "paramètre de sortie") qu'il ne doit pas désalloué les allocations qui ont été faites (sans appel d'une fonction C). On peut dire qu'il y a là un "transfert de propriété" de la fonction B appelée, à la fonction A qui a appelé la fonction B. Ce que je nomme paramètre de sortie, pourrait se nommer "tranfert de propriétés" de B (la fonction appelée) vers A (la fonction appelante), mais c'est juste une "valeur de retour ou un paramètre de sortie" (obligatoire via la syntax) qui se passe. C'est la séparation entre les valeurs d'entrée entre les () et les valeurs de retours, placées après l'opérérateur <- que cela est possible. C'est de plus très naturelle, car toutes les fonctions utilisent ce mécanisme pour récupérer le résultat d'une fonction.

    Citation Envoyé par koala01 Voir le message
    Et j'en reviens à ma question: que se passe-t-il si la fonction qui appelle foo s'attend à récupérer la ressource Si vous ne pouvez pas garantir que la fonction appelante sera en mesure de récupérér une ressource valide et, par définitive, d'en devenir le propriétaire légitime, tout votre design devra être adapté en conséquence... Avec une complexité bien supérieure à ce que vous permettrait le fait d'accepter le transfert de propriété.
    C'est exactement ce qui se passe, la fonction A, en apellant le fonction B, force (via la syntax), que B assigne (on peut appeler cela transfert de propriétaire si l'on veut), mais c'est juste une assignation.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fct B(int8 size) <- int8 p<>:
       int8 d_bloc<idx=size> ;allocation dynamique
       p := d_bloc                 ; pas désallocation car p est un "paramètre de sortie", car après le '<-'
    end
    
    fct A():
       int8 tab<>
       tab := B(100) ; A est le "proriétaire" de tab
    end                   ; tab sera ici désalloué.
    Lors de l'appel
    C'est tab qui sera utilisé, (sous le nom p) comme paramètre de sortie.
    Comme la syntax de la fonction B retourne p, A est certains de récupérer tab.
    'p' est tab, via l'assignation de la valeur de retour. Il n'y a là aucune complexité, ni pour le compilateur, ni pour l'utilisation dans le langage.

    Citation Envoyé par koala01 Voir le message
    La question reste malgré tout la même: qu'en est il de la fonction qui fera appel à foo, en particulier si elle souhaite récupérer la ressource allouée par foo

    Dites moi que vous ne voulez pas que la fonction qui appelle foo puisse récupérer la ressource, que vous comprenez les défis auxquels cette décision vous fera faire face, je vous foutrai la paix, et je vous laisserai vous dépêtrer avec vos problèmes (quitte à essayer de vous aider à vous en sortir).
    Au contraire, la fonction foo peut récupérer la ressource, c'est ce qui est démontré dans le code ci-dessus. C'est une solution simple et élégante, et toutes les fonctions fonctionne de la même manière. Vous pouvez appeler cela "un transfert de propriété" de B vers A, mais je n'aurais même pas à introduire la notion de "propriété" aux utilisateurs du langages, ce sera juste un appel de fonction dont le résultat est assigné à tab.

    Je pense que c'est juste une question de vocabulaire qui fait que l'on semble ne pas se comprendre, me semble-t-il. Cette manière de faire sécurise la désallocation, sans devoir introduire les notions d'ownership, de borrowing et de borrow checker.

    C'est une solution que me semble plus simple, pratiquement transparente même.

    Citation Envoyé par koala01 Voir le message
    C'est justement là le problème :pour que la ressource allouée dans votre fonction et ajoutée à votre tableau ne soit libérée que lorsque le tableau est détruit, il faut ... transférer la propriété de la ressource au tableau.

    Autrement, c'est la fonction qui s'occupe de l'allocation de la ressource qui, en tant que propriétaire, doit s'assurer de la libérer correctement.

    Cette phrase à elle seule contredit tout ce que vous dite concernant l'impossibilité de transférer la propriété et valide tout ce que moi je peux dire concernant l'absolue nécessité d'autoriser le transfert de propriété.
    C'est bien une question de vocabulaire. Ce que vous nommez "transfert" de propriété, est pour moi juste une "assignation". l'aspect sécuritaire étant gérée par le fait de les paramères l'opérateur <- ne seront pas désalloués.

    Citation Envoyé par koala01 Voir le message
    Même pas... la responsabilité de libération des éléments du tableau doit échoire ... au tableau lui-même, pas au propriétaire du tableau. Car, autrement, cela voudrait dire que le seul moyen de supprimer un élément et de s'assurer que la ressource sera correctement libérée serait de le faire ... dans la fonction dans laquelle le tableau existe.
    Les éléments du tableau "appartiennent" bien au tableau, qui peuvent être ajoutés/retirer/modifier même s'il est prèté à une autre fonction. Elle l'utilise.
    Mais le tableau lui-même appartient au scope dans lequel il a été créer. Les éléments qui seraient toujours dedans, seront supprimés à la fin du scope qui a créer le tableau.

    Citation Envoyé par koala01 Voir le message
    Justement, non...
    L'ajout d'un élément au tableau est un transfert de propriété, de la fonction qui crée l'élément vers le tableau qui le contient.
    On peut appeler cela un transfert de propriété dans votre vocabulaire, mais c'est juste un "ajout" dans le mien ;-) Je pense que l'on parle de la même chose, mais avec des termes différents. Et je respecte tout à fait que vous appelliez cela "un transfert de propriété".

    Citation Envoyé par koala01 Voir le message
    Pourtant, en décidant qu'une fonciton ne peut pas transmettre la propriété de la ressource qu'elle alloue à la fonction qui l'a appelée, vous placez un énorme verrou qu'il vous sera difficile de contourner.
    Il n'y a aucun verrou, car la fonction appelée peut "transférer" la propriété, ce que je n'ai pas besoin d'expliquer en ces termes, je dois juste parler d'appel de fonction, qui retourne une ou des valeurs (allouée dynamiquement ou pas), qu'il faut bien "assigner" à des varaibles pour les récupérer.

    Citation Envoyé par koala01 Voir le message
    Et c'est tout à votre honneur!

    Le point est que, en l'état, votre réflexion me semble "manquer de profondeur", sans doute à cause d'un manque d'expérience, car vous n'envisagez pas un cas qui se présentera bien plus souvent que vous ne semblez le croire.
    Merci, j'espère avoir plus correctement expliqué le "fonctionnement" de mon langage sur ce point. Le cas qui se présentera "bien plus souvent que je ne semble l'imaginer" est tout a fait gérable, presque "naturellement". Je ne prend absolument pas ombrage lorsque vous dites que ma réflexion "manque de profondeur, sans doute à cause d'un manque d'expérience". J'aime être critiqué poliment, je n'ai pas de soucis avec cela. Je n'ai en effet pas d'expérience au niveau de la définition ou de la création de compilateur. J'ai par contre énormément d'expérience en temps qu'utilisateur de divers langage (en C surtout), et je tente, à partir de cette expérience, de définir "le plus simplement possible" un langage "extrèmement lisible", (car on lit bien plus souvent du code qu'on en écrit) sans y introduire de complexités ou de "termes" inutile.

    Citation Envoyé par koala01 Voir le message
    C'est pourtant ce que vous faites en essayant de définir votre propre langage. C'est ce que fait n'importe qui en essayant de définir un langage : définir un ensemble de règles permettant à un outil de comprendre ce que l'on attend de lui.
    J'espère vous avoir convaincu du contraire dans cette, en créant une syntaxe simple, lisible, et sans utiliser des "conceptes" inutiles ou inutilement trop compliqués.

    Citation Envoyé par koala01 Voir le message
    Et c'est une excellente chose.

    Cela ne doit juste pas se faire en mettant au placard des pratiques au sujet desquelles cinquante ans d'évolution dans les langages ont clairement démontré qu'elles étaient incontournables.
    Je ne mets pas "au placard" des notions qui sont effectivement incontournables, je tente juste de faire que ces notions soient le plus simplement possible utilisables ou a mettre en pratique. J'espère que j'ai mieux expliqué comment ces notions sont bien présentent dans mon langage, et qu'elle sont plus facilement compréhensibles qu'en utilisant des notions plus compliquées que nécessaire.

    Je n'affirme pas que je ne vais pas tomber à un moment ou a un autre sur des concepts incontournables que la syntaxe de mon langage ne permetrait pas d'utiliser. Je reste humble face au défi que je me suis donné. Mais je suis très "persévérant", et s'il faut ajouter,changer ou complexifier la syntaxe, je le ferais, mais en essayant toujours de garder une syntaxe la plus simple possible.

    Citation Envoyé par koala01 Voir le message
    C'est vrai lorsque vous êtes au niveau d'une fonction appelante... Mais une fonction ne vaut que si elle est appelée. La question est donc toujours la même : que se passe-t-il si la fonction est appelée par une fonction qui souhaite récupérer la ressource allouée
    Je pense avoir répondu à cette question ci-dessus, mais si vous y voyez toujours un blocage ou une limite, n'hésitez pas à m'en faire part. Cet échange est très instructifs, et je vous en remercie non seulement d'y participer, mais également de le faire courtoisement.

    Citation Envoyé par koala01 Voir le message
    L'initiative est louable, certes, mais elle pose une série de problème qui seront bien plus difficile à prendre en compte et à résoudre que le simple fait d'accepter l'idée que la propriété d'une ressource allouée par une fonction puisse être prise en charge par (et donc transférée à) la fonction ayant appelé la fonction responsable de l'allocation de la ressource.
    Idem, je pense avoir répondu à celà. Sans utiliser le même vocabulaire que vous, d'où l'incompréhention. C'est ce qu'il me semble en tout cas.

    Citation Envoyé par koala01 Voir le message
    Le fait est que this ou self ne sont pas "vraiment" des paramètres de sortie. Enfin, si, techniquement parlant, ce sont bel et bien des paramètres de sorite. Seulement, on entre là dans le domaine des fonctions membres. Des fonctions qui n'existent que parce qu'il existe un type de donnée à partir duquel les appeler, et qui ne peuvent être appelées qu'au départ d'une instance du type de donnée en question.

    Lorsque la fonction membre modifier les données internes de l'objet à partir duquel elle est appelée, il y a bien "effet de bord", cependant, il reste malgré tout limité, et s'il y avait moyen de l'éviter (entre autres, pour les types de données ayant sémantique de valeur), ce serait pas plus mal.
    La POO est permise par mon langage, elle n'est pas faite d'une manière "conventionnel", mais d'une manière très partique et simple, en se basant plus sur les actions que peut faire un Type (leur interface), plûtot que sur les données du Type. Elle reste simple a mettre en place, tant au niveau du compilateur qu'au niveau du langage. Mais ne rentrons pas dans ce débat maintenant. Mais si je devais le résumer en qlq mots, je dirais qu'une simple structure (struct en C), peut facilement devenir un nouveau Type, qui peut facilement devenir une 'class' et qui peux/doit implementer certaines "méthodes" (pour respecter une interface dont il peut se revendiquer), et ces les types respectant la même interface peuvent être utilisé dans même savoir les donnée qu'ils contiennent pour parvenir a satisfaire la ou les interfaces.

    Encore merci de votre participation.

    BàV et Peace & Love.

  13. #33
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Bonjour fdecode
    Cela faisait un petit moment que nous n'avions plus échanger.

    Citation Envoyé par fdecode Voir le message
    Si vous creusez l'idée, cela vous amène doucement à une sémantique de prêt à la Rust.
    Là vous vous en passez parce que vos exemples sont essentiellement mono-thread et non-concurrent. -> Je prête ma propriété et j’attends le retour de fonction pour récupérer ma propriété.
    Oui, cela se rapproche du "Borrowing" de "Rust", car la notion "d'Ownership" elle même semble vite montrer ses limites. C'est pour cela que je ne parle pas trop de la notion de "propriétaire".

    Citation Envoyé par fdecode Voir le message
    Avec votre sémantique, je ne suis même pas sûr que vous puissiez prêter ce que vous avez emprunté.
    Si, cela est possible, le "prêt" d'une ressource qu'une fonction a elle même reçue en prêt, peut se faire exactement de la même manière que le premier "prêt", en devant le récupérer. On peut "empiler les prêts", qui seront retournés lors du "désempilage". Au final, le prêt initial sera retourné à la fonction qui a "créé" l'allocation, et elle seule pourra en faire la désallocation.

    Citation Envoyé par fdecode Voir le message
    Mais considérons maintenant des exemples avec appels concurrents ou multithreads, alors le processus appelant peut continuer de s'exécuter: quelles sont les règles d'utilisation des données déjà prêtées?
    Comment faites vous pour prêter à plusieurs processus? (ce qui peut être utile pour partager des données entres plusieurs threads)
    Voilà une question très intéressante. Il y a deux approches possibles. Soit le "multitasking" n'est pas autorisé, soit il l'est et il faut dès lors le gérer.

    Un petit rappel. L'idée de base de tout mon travail repose sur le fait d'imiter le fonctionnement d'un micro-ordinateur du genre de ceux que l'on utilisait dans les années 80, mais via une machine virtuelle (qui "interprétera le code assembleur binarisé que produira le compilateur"). Mon ambition s'arrête là, mais cela ne veut pas dire que je n'apporterais pas "une touche de modernité" en créant un "kernel" (c'est cette voie là que je compte suivre) sur lequel le langage pourra s'appuyer.

    C'est pour cela que je dis qu'il y a deux approches possibles, tout en sachant que les "kernels" (les mini OS équipant ces anciennes machines) étaient eux "mono-tâche".

    Le "µkernel" sera en fait un mini-RTOS (real-time Operating System). J'en connais parfaitement le fonctionnement car j'en ai déjà créé un pour un petit micro-controlleur disposant uniquement de 16Kb de RAM. Ce RTOS ne demandait pas énormément de ressource (c'était pour un système embarqué), et "tenait" dans quelques Ko.

    Comme tout vrai RTOS (préemptif, et non coopératif comme windows l'était à ses début (avant NT)), le Scheduler exécute la tâche la plus prioritaire, et évite d'avoir recours à la classique "main loop" que l'on retrouve (trop) souvent dans les firmware, qui empêche une maîtrise temporelle rigoureuse, comme le permet un RTOS.

    Au-delà de la gestions des "tasks", un RTOS utilise (notamment) des mutex, qui permettent qu'une ressource ne soit utilisées que par une "task" à la fois. Les autres "task" étant misent en pause jusqu'au moment où des tâches plus prioritaires "libèrent" la ressource. Les "allocations" faites dans une fonction se feront via ce "µKernel RTOS", qui permettra l'utilisation de la même ressource, mais pas "en même temps", par plusieurs "tasks".

    Dans un RTOS, il y a également ce que l'on nomme des "mailbox", qui permettent l'échange de message et/ou de ressources entres les différentes "tasks".

    Par rapport à une simple "main loop", un RTOS permet de n'effectuer une "task" que lorsque c'est nécessaire. Il est même assez facile de voir quel pourcentage temporelle est utilisé par tel ou tel task. En passant d'une "main loop" à un "RTOS", on remarque assez vite que le "temps processeurs" est inutilement gaspillé via une "main loop" qui occupe le CPU à 100% bien souvent, là où avec un RTOS les tasks de l'application ne doivent en fait qu'utiliser que qlq % du temps CPU. C'est crucial dans le monde de l'embarqué, parce que cela permet de mettre le micro-controlleur dans un état "low-energie", et de le "réveiller" uniquement lorsque c'est nécessaire, et permet d'économiser formement la consommation.

    Bref, je compte donc me reposer sur ce µKernel RTOS pour gérer le multitasking et le partage des ressources (et donc des pointeurs).

    C'est l'idée et je vous concède que je n'irais pas jusqu'à l'utilisation (ou imitation) de CPU ayant plusieurs "Core".

    BàV.
    Et Peace & Love.

  14. #34
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Oui et non...
    Citation Envoyé par fdecode Voir le message
    Du coup, cela m'amène à me replonger dans d'anciens messages.

    Votre affirmation n'est pas claire du tout pour moi. Qu'est-ce que vous entendez par là.
    Si je reprends votre exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // définition d'une fonction:
    fct do_something(int8 a, b) <- int16 sum, int16 mul:
        sum := a + b
        mul := a * b
    end
     
    int16 r1, r2
    r1, r2 := do_something(10, 6)
    Cela ressemble à un code Rust de la forme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // définition d'une fonction:
    fn do_something(a: &i16, b: &i16, sum: &mut i16, mul: &mut i16) {
        *sum = a + b;
        *mul = a * b;
    }
     
    let [mut r1,mut r2] = [0,0];
    do_something(&10, &6, &mut r1, &mut r2);
    En d'autre termes, c'est comme si vous utilisiez des références non mutables et mutables, mais de manière implicite par la structuration de vos fonctions.
    Le résultat est le même, en effet, mais il me semble plus lisible et même "tranparent", car il repose sur la notion d'assignation, et la séparation entre les valeur passées en argument entre les () et ceux passés en argument après l'opérateur <-, le tout sans avoir besoin d'introduire d'autres "concepts".

    Cela me semble plus "simple", lisible et compréhensible.

    Mais je reste humble, et si je commet une erreur dans le système employé, où que ce système empêche certaines fonctionnalités, j'en accepterais le fais et je retournerais sur la "planche à dessin" pour voir comment résoudre ces soucis.

    BàV et Peace and Love.

  15. #35
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Je ne dis pas que c'est le "scope de validité", j'impose juste que c'est ce scope et lui seul, qui pourra effectuer la désallocation.
    Mais ca revient strictement au même...

    Car cela implique que vos ressources devront quoi qu'il advienne être libérée au moment où l'exécution atteint le end de votre fonction.

    Car, de deux choses l'une :
    1. ou bien, la fonction qui alloue une ressource est forcément responsable de la libération de cette ressource au plus tard lorsque la fonction s'achève, ce qui implique un système proche du RAII en C++
    2. ou bien, vous acceptez de transférer la propriété de la ressource-- et donc la responsabilité de libéréer-- à la fonction appelante.

    Mais, si vous refusez la deuxième solution et que vous n'appliquez pas strictement la première, vous aurez forcément des fuites mémoires pour toutes les ressources allouées de manière dynamique.
    Je comprend ce que vous dites, le problème, c'est qu'apparemment je ne vois pas quelles seraient ces complexités.
    C'est à vrai dire à ce niveau que l'expérience fait toute la différence ... Si nous en reparlions d'ici six mois ou un an Je suis sur que, d'ici là, vous aurez été confrontés aux problèmes contre lesquels j'essaye de vous mettre en garde

    Je pense que l'on ne se comprend pas sur ce point. Si une fonction A appel une fonction B (qui fait l'allocation), elle récupére le "pointeur" via un "paramètre de retour",
    Sauf que, selon votre description, c'est la fonction qui s'occupe de l'allocation de la resource qui en est propriétaire...

    Cela implique qu'il est de la responsabilité de la fonction qui s'occupe de l'allocation de la ressource de s'occuper de la libération de la ressource.

    Le problème, c'est qu'elle devra le faire au plus tard ... à la fin de son exécution, si bien que le paramètre de retour fait référence ... à une ressource qui a été libérée...

    A moins que vous n'acceptiez que la propriété de la ressource -- et la responsabilité de la libérer qui va avec -- ne puisse effectivement être transférer -- au travers du paramètre de retour si vous y tenez -- à la fonction appelante.

    Vous pouvez retourner le problème dans tous les sens et jusqu'à la fin des temps: la propriété d'une ressource va de pair avec la responsabilité de sa libération. C'est d'ailleurs la raison d'être de cette notion de propriété : savoir qui aura la responsabilité de libérer la ressource.

    Si vous ne transférez pas cette responsabilité à la fonction appelante, à la fonction qui a fait appel -- éventuellement en fournissant une donnée qui lui est propre et qui servira de paramètre de retour -- à la fonction qui s'occupe d'allouer la ressource, le seul endroit où vous pouvez faire jouer cette responsabilité de libération des ressources se trouve ... au niveau de la fonction qui s'occupe de l'allocation de la ressource.
    C'est exactement ce qui se passe, la fonction A, en apellant le fonction B, force (via la syntax), que B assigne (on peut appeler cela transfert de propriétaire si l'on veut), mais c'est juste une assignation.
    Justement, non, ce n'est pas "juste une assignation".

    "juste une assignation", c'est le phénomène que vous obtenez avec un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int a = 15; // "juste une assignation ici : a vaut 15
    int b = a; // encore une assignation ici : b a la même valeur que a et vaut donc 15
    Ca, c'est "juste une assignation".

    Represons votre code, avec mes propres commenaires, si vous le voulez bien:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    fct B(int8 size) <- int8 p<>:
       int8 d_bloc<idx=size> ;allocation dynamique
       ; suite à l'allocation dynamique, B a pris la resonsabilité
       ; et donc "la propriété" de la ressource connue sous le nom de d_bloc
       ; mais, comme la ligne suivante
       p := d_bloc
       ; assigne la ressource à un paramètre de retour,
       ; c'est la fonction à laquelle appartient ce paramètre qui
       ; qui prend la responsabilité de la libération, et donc, la propriété de fait
       ; de la ressource
    end
     
    fct A():
       int8 tab<>
       tab := B(100) ; A est le "proriétaire" de tab
    end                   ; tab sera ici désalloué.
    Voilà, ce qui se passe réellement...

    Il ne s'agit pas de
    on peut appeler cela transfert de propriétaire si l'on veut
    Il s'agit d'appeler un chat un chat; il s'agit d'appliquer les bons termes aux bons phénomènes. Et le fait est que, dans ce code, ce qui "pourrait passer pour une assignation" car il y ressemble à s'y méprendre est en réalité ... un transfert de propriété

    Vous pouvez encore une fois tourner cela dans tous les sens, vous en reviendrez toujours au fait que, lorsque vous empêchez la fonction qui s'occupe d'allouer une ressource d'appliquer son devoir de propriétaire en libérant la ressource au profit de la fonction appelante, vous effectuez bel et bien... un transfert de propriété...

    Il est possible que vous n'en ayiez pas conscience, ou -- j'espère -- que vous n'en ayiez pas eu consicence plus tôt, mais le fait est bel et bien là : il s'agit effectivement d'un transfert de propriété et non "d'un simple prêt" et encore moins "d'une simple assignation"...
    Là, vous avez effectivement "assignation". Mais, encore une fois, ce n'est pas "juste une simple assignation", mais bien une "assignation avec transfert de propriété"...

    Au contraire, la fonction foo peut récupérer la ressource, c'est ce qui est démontré dans le code ci-dessus. C'est une solution simple et élégante, et toutes les fonctions fonctionne de la même manière. Vous pouvez appeler cela "un transfert de propriété" de B vers A, mais je n'aurais même pas à introduire la notion de "propriété" aux utilisateurs du langages, ce sera juste un appel de fonction dont le résultat est assigné à tab.
    Oui, maintenant j'ai compris votre idée...

    Sauf qu'elle occasion effectivement un transfert de propriété, quoi que vous puissiez dire

    C'est bien une question de vocabulaire. Ce que vous nommez "transfert" de propriété, est pour moi juste une "assignation". l'aspect sécuritaire étant gérée par le fait de les paramères l'opérateur <- ne seront pas désalloués.
    Mais, par pitié, essayez d'appeler un chat un chat...

    Une "simple assignation" est le fait ... d'assigner une valeur à une variable. Si, en même temps que vous assignez une valeur à une variable, vous lui donnez la responsabilité de libérer une ressource, vous lui ... transférez la propriété de cette ressource.
    On peut appeler cela un transfert de propriété dans votre vocabulaire, mais c'est juste un "ajout" dans le mien
    Pour que ce soit "juste un ajout", il faut ad minima que le tableau dispose déjà de l'espace nécessaire pour "stocker" le nouvel élément.

    Mais, si, pour une raison ou une autre, il faut augmenter la quantité de mémoire allouée au tableau de manière à pouvoir ajouter un élément, il y a tout un mécanisme à mettre en place qui, lui, ne pourra se faire qu'à coup de ... transfert de propriété...

    Vous pouvez (et vous le faites peut être déjà ) que je pinaille, que j'apporte une trop grande importance aux différents termes, ou même que j'insiste sur des détails de vocabulaire...

    D'une certaine manière, je ne peux même pas vous donner tors : j'insiste effectivement beaucoup sur l'utilisation du bon terme pour désigner la bonne chose.

    Le fait est que le développement informatique ne nous donne pas le choix : si on n'arrive pas à exprimer correctement les choses dans notre langage "natif" (le français, pour ma part), il ne faut pas s'étonner si quelque chose d'aussi bête qu'un ordinateur, quel que soit le niveau "d'intelligence" que nous pourrons à donner à l'outil qui nous permettra de dialoguer avec lui, n'est pas capable de nous comprendre correctement et de nous fournir le résultat qu'on attend de sa part.

    On peut -- bien sur -- choisir des termes différents lorsqu'ils représentent le même concept, comm "structure" ou "enregistrement", comme "fonction" ou "procédure" ou comme "paramètre" ou "argument" (quoi qu'il y ait une légère nuance entre les deux derniers). Par contre, on ne peut pas parler de "prêt" et encore moins de "simple assignation" lorsque ce qui se passe est clairement un "transfert de propriété. Parce que les deux choses sont totalement différentes.

    Il n'y a aucun verrou, car la fonction appelée peut "transférer" la propriété, ce que je n'ai pas besoin d'expliquer en ces termes, je dois juste parler d'appel de fonction, qui retourne une ou des valeurs (allouée dynamiquement ou pas), qu'il faut bien "assigner" à des varaibles pour les récupérer.
    En m'intéressant un peu à votre code, j'ai effectivement fini par le comprendre...

    Cependant, il faut arriver à vous convaincre que ce que vous faites n'est pas qu'une "simple assignation", mais bel et bien un "transfert de responsabilité", et donc, un "transfert de propriété". Vous pouvez me croire sur parole, le simple fait de faire la distinction entre "une simple assignation" et "un transfert de propriété" rendra les choses beaucoup plus claires pour vous par la suite...

    J'espère vous avoir convaincu du contraire dans cette, en créant une syntaxe simple, lisible, et sans utiliser des "conceptes" inutiles ou inutilement trop compliqués.
    Mais même une "syntaxe simple" n'est jamais qu'un ensemble de règles qu'il faut suivre d'un coté pour pouvoir l'appliquer "à rebours" de l'autre.

    La meilleure preuve en est que si vous ne respectez pas la syntaxe -- aussi simple puisse-t-elle être, l'outil qui doit appliquer les règles se plaindra et s'arrêtera sur une erreur. Que peut on rêver comme plus belle preuve du fait que c'est effectivement une question de règles

    Si l'on parle de "conventions", ce n'est au final que pour nous donner l'impression qu'elles ne nous sont pas imposées, mais qu'elles sont acceptées d'un commun accord. Mais il n'empêche qu'il s'agit effectivement de règles qui, si elles ne sont pas respectées, produiront forcément une erreur.

    Encore une fois, on peut tourner les choses comme on veut, les faits sont ce qu'ils sont

    Je ne mets pas "au placard" des notions qui sont effectivement incontournables,
    Non, en fait, je me suis rendu compte avec votre dernière réponse que vous les appliquiez sans même vous en rendre compte, parce que vous les assimiliez à des termes qui n'ont absolument rien à voir, et que, du coup, vous pensiez "prêt" alors que c'est bien plus que cela

    La POO est permise par mon langage, elle n'est pas faite d'une manière "conventionnel",
    Ah, ben, changeons donc de sujet...

    Pour vous, qu'est ce que la POO Quel en est le principe fondamental

    Car, les réponses à ces deux questions auront très certainement une influence majeure sur la manière même dont vous envisagerez votre langage... Dés lors, autant vous aider à l'envisager "le plus correctement possible"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  16. #36
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    339
    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 : 339
    Par défaut Ce n'est que mon opinion...
    Citation Envoyé par koala01 Voir le message
    Mais ca revient strictement au même...
    Je pense que nous commençons à nous comprendre. Techniquement parlant (et j'en suis bien conscient depuis le début, puisse que je tente via cette syntaxe, d'éviter d'introduire des concepts "trop compliqués" pour un débutant, comme ceux introduits par Ada ou Rust), oui, il y a un "transfert de propriété". Mais au niveau de l'utilisateur, cela apparaît comme une simple assignation.

    Pour ce qui est de la syntaxe, elle me semble bien plus simple et lisible que le même code en "Rust" ou en C++. (C'est mon avis), tout en sécurisant les allocations dynamique, et leur désallocations automatique au bon moment.

    Je veux bien passer et parler de ma vision de la POO, mais pas directement maintenant. Mais j'ai reviendrai, soyez en certains.

    Encore merci d'avoir pris la peine de comprendre mon approche.

    Nous nous reparlerons bientôt.

    BàV et Peace & Love.

  17. #37
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    402
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 402
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    elle me semble bien plus simple et lisible que le même code en "Rust" ou en C++. (C'est mon avis), tout en sécurisant les allocations dynamique, et leur désallocations automatique au bon moment
    Citation Envoyé par OuftiBoy
    L'idée de base de tout mon travail repose sur le fait d'imiter le fonctionnement d'un micro-ordinateur du genre de ceux que l'on utilisait dans les années 80
    Citation Envoyé par OuftiBoy
    Veuillez m'excuser, mais je ne vois (actuellement) pas de complexité, ni dans le code du compilateur, ni dans l'utilisation du langage.
    Il faut quand même comprendre que la complexité des langages vient de la complexité du hardware (multicores, multiples mémoires a pleins de niveaux, etc), du software (systeme d'exploitation), et des besoins (des logiciels de plus en plus complexes, de plus en plus connecté). Donc forcément qu'en te plaçant dans le contexte d'un ordi des années 80, tu peux avoir un langage de programmation qui sera plus simple, plus lisible, plus sur.

    Mais en fait, tu refais juste le BASIC des années 80. Le C++ et Rust sont complexes parce qu'on a besoin qu'ils soient complexes. Tout au moins qu'ils puissent faire des choses complexes. Comme je l'ai dit dans un message précédent, tes exemples sont trop simples et cela ne te permet pas de voir que ton langage a (probablement) trop de limitations pour être un langage généraliste au même titre que le C++ ou Rust.

    Si c'est ce que tu veux, c'est ok, mais alors c'est pas du tout le même contexte que pour le C++ et Rust et la comparaison entre ces langages et ton langage n'a aucune pertinence. (Dit autrement, c'est comme si tu disais que ton tournevis est plus simple et plus sûr que ma perceuse. Ok. Mais c'est osef comme comparaison)

  18. #38
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    103
    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 : 103
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Bonjour à tous, par demande j'ai créé un autre fil de discution: qui se trouve ici:

    https://www.developpez.net/forums/d2.../#post12086863
    Il n'a pas l'air de marcher.

  19. #39
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    103
    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 : 103
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Comme je l'ai dit dans un message précédent, tes exemples sont trop simples et cela ne te permet pas de voir que ton langage a (probablement) trop de limitations pour être un langage généraliste au même titre que le C++ ou Rust.
    Si on rajoute des pointeurs comptés (genre Arc) et des mutex, et si on conçoit que sa grammaire revient à utiliser des références mutables et non mutables, on pourrait faire beaucoup de choses.
    Par contre, où serait créée, gérée et détruite la donnée pointée par Arc? Cela transgresserait l'idée de gérer la propriété de la donnée au niveau de la fonction qui crée la variable.

    Citation Envoyé par mintho carmo Voir le message
    Mais en fait, tu refais juste le BASIC des années 80. Le C++ et Rust sont complexes parce qu'on a besoin qu'ils soient complexes.
    Oui, mais même avec une architecture monothread, on peut faire de la programmation concurrente (async de Rust, coroutine...).

  20. #40
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Je pense que nous commençons à nous comprendre. Techniquement parlant (et j'en suis bien conscient depuis le début, puisse que je tente via cette syntaxe, d'éviter d'introduire des concepts "trop compliqués" pour un débutant, comme ceux introduits par Ada ou Rust), oui, il y a un "transfert de propriété". Mais au niveau de l'utilisateur, cela apparaît comme une simple assignation.
    Java a commis cette erreur avant vous, en faisant croire que l'implémentation d'une interface était un mécanisme tout à fait différent de l'héritage public, alors que les deux ne sont finalement que les deux faces d'une même pièce : le "sous typage" tel que défini par le LSP...

    Et maintenant, il est très difficile de faire comprendre leur erreur aux développeurs qui se sont spécialisés dans ce langage.

    Profitez des erreurs des autres pour ne pas les reproduire, car qui oublie les erreurs du passé est condamné à les reproduire

    Simplifiez tant que vous voulez, mais, surtout, évitez de mentir sur les concepts que vous mettez en oeuvre.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. Réponses: 3
    Dernier message: 04/05/2024, 22h30
  2. Noulith : un nouveau langage de programmation construit sur Rust
    Par Bruno dans le forum Langages de programmation
    Réponses: 1
    Dernier message: 08/01/2023, 20h41
  3. Nouveau langage, conseils sur la grammaire
    Par Christophe Genolini dans le forum ALM
    Réponses: 0
    Dernier message: 21/08/2013, 10h09
  4. Nouveau langage : le D
    Par cheick dans le forum D
    Réponses: 4
    Dernier message: 30/05/2004, 15h56

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