Clément V a écrit 95 commentaires

  • [^] # Re: Croyances

    Posté par  . En réponse au lien Falsehoods Programmers Believe About Names – With Examples. Évalué à 3.

    J'ai l'impression que c'est le pronom relatif implicite qui te dérange : « Falsehoods (that) programmers believe about names » → « Erreurs auxquels les programmeurs croient au sujet des noms »

  • [^] # Re: Dommage que ce ne soit pas pour DNF

    Posté par  . En réponse au lien yum history. Évalué à 3.

    Il y a des chances que yum ne soit qu'un alias de dnf et que donc tu utilisais déjà dnf sans le savoir. Sur Fedora 32 en tout cas :

    $ readlink -f $(which dnf)
    /usr/bin/dnf-3
    $ readlink -f $(which yum)
    /usr/bin/dnf-3
    
  • # uBlock Origin bloque les trackers déguisés

    Posté par  . En réponse au lien Le déguisement des trackers par Cname. Évalué à 2.

    Je ne connaissais pas la technique mais je suis rassuré : uBlock Origin a déjà une solution contre celle-ci (https://github.com/gorhill/uBlock/releases/tag/1.25.0).

  • [^] # Re: Firefox a eu sa chance par le public

    Posté par  . En réponse au journal Hégémonie et navigateurs. Évalué à 1.

    Sync n'est toujours pas complet, il manque les moteurs de recherche : https://bugzilla.mozilla.org/show_bug.cgi?id=444284 12 ans que ça dure !

  • [^] # Re: Typage structurel

    Posté par  . En réponse au journal C++ Hell/Heaven et les concepts. Évalué à 3.

    Pour compléter les autres réponses, il y a un mécanicisme un peu plus complexe mais plus puissant (et qui est celui utilisé dans le journal). Il se base sur une règle du C++ abrégée en SFINAE (Substitution Failure Is Not An Error). L'idée est que si, lors de la substitution d'un type dans un template, on obtient une déclaration que n'a pas de sens, le compilateur ignore la fonction au lieu d'émettre une erreur. Ça ne marche que dans la déclaration (paramètres du template, type de retour, ou types des paramètres), pas dans le corps de la fonction.

    #include <iostream>
    
    template<typename T>
    typename T::A f(T) {
        std::cout << "J'ai un type imbriqué A" << std::endl;
        return {};
    }
    
    template<typename T>
    typename T::B f(T) {
        std::cout << "J'ai un type imbriqué B" << std::endl;
        return {};
    }
    
    struct Type1 {
        using A = int;
    };
    
    struct Type2 {
        using B = int;
    };
    
    int main() {
        f(Type1{});
        f(Type2{});
        return 0;
    }

    Ce qui donne :

    J'ai un type imbriqué A
    J'ai un type imbriqué B

    Pas d'erreurs puisqu'à chaque fois exactement une des fonctions est gardée. Si un type avait à la fois une type imbriqué A et B, les deux fonctions seraient valide et le compilateur générerait une erreur comme il ne saurait pas choisir. Si un type n'a aucune des deux propriétés, les deux fonctions sont ignorées et le compilateur génère une erreur disant que f n'existe pas (mais s'il est sympa, il t'explique pourquoi il a ignoré les deux possibilités avec un message à rallonge).

    Ça permet d'avoir des fonctions surchargées qui sélectionnent la bonne alternative en fonction des propriétés d'un type paramètre du template. Évidemment, si la condition que tu veux avoir n'est pas un type que tu utilises en retour ou en paramètre, ça devient plus compliqué mais beaucoup de choses sont possibles. Les concepts de C++20 simplifient largement tout ça.

    Pour aider à l'utilisation avancée du SFINAE pré-C++20, il y avait quelques aides comme enable_if et une TS proposait d'ajouter des détecteurs (mais c'est sûrement devenu obsolète avec les concepts).

  • [^] # Re: Quel est l'intérêt ?

    Posté par  . En réponse au journal C++ vin va vous faire tourner en barrique !. Évalué à 3.

    Perso je l'utilise par exemple suivant si l'utilisateur veut une version Unicode ou pas de ma lib, utile à une époque même si moins de nos jours, je ne vois pas comment proposer ça simplement avec les modules, à part faire comme Java par avoir les 2 interfaces pour tous et limiter la taille du bousin est un truc que j'aime en C++ justement).

    Si j'ai bien compris, tu as toujours le droit d'utiliser des #ifdef, mais les defines sont limités au module qui en a besoin. Plus besoin d'ajouter un -DZENITRAM_UNICODE pour la compilation de tous les fichiers qui pourraient inclure directement ou indirectement tes headers. Le revers c'est que les modules ne peuvent plus exporter de macros.

  • [^] # Re: Est-ce qu'il y a....

    Posté par  . En réponse au lien Micro: enfin un éditeur de texte normal pour votre terminal?. Évalué à 4.

    Une fonctionnalité nécessaire pour devenir le meilleur éditeur de texte (aussi appelé vim).

    Plus précisément, une façon très pratique de sélectionner des bouts de textes.

  • [^] # Re: Bien sûr

    Posté par  . En réponse au journal Toujours plus de fun avec C. Évalué à 5.

    Non, Perl (source : https://xkcd.com/224/).

  • [^] # Re: Comportement indéfini

    Posté par  . En réponse au journal C++, surcharge d'opérateur, ordre d'évaluation. Évalué à 1.

    Je ne savais que la règle avait changé. C'est quand même étrange de voir = comme un point de séquence.

    Mais là -std=c++11 est passé au deux compilateurs, donc on est bien dans un cas où l'ordre est indéfini, non ?

  • # Comportement indéfini

    Posté par  . En réponse au journal C++, surcharge d'opérateur, ordre d'évaluation. Évalué à 8.

    m[1] = m.size();

    L'ordre entre m[1] et m.size() n'est pas déterminé. operator= est exécuté en dernier mais ce n'est pas lui qui ajoute l'élément, c'est m[1].

  • [^] # Re: Haskell super expressif ?

    Posté par  . En réponse au journal Comprendre Go en 5 minutes, en Haskell. Évalué à 1.

    En effet, l'intérêt est limité. Mais il faut aussi se poser la question dans l'autre sens : quels sont les désavantages des fonctions curryfiées et quels sont les avantages des fonctions prenant un tuple de tous les arguments en même temps ?

    J'en vois assez peu. Le principal, comme le montre l'existence de ce fil, c'est que ça perturbe ceux qui ne connaissent pas. Donc dans la plupart des langages, les fonctions curryfiées sont à éviter sauf quand c'est vraiment utile. Mais dans les langages dans lesquels c'est idiomatique (comme le Haskell ou d'autres langages fonctionnels), ça ne coûte rien de curryfier par défaut et de n'utiliser les tuples seulement quand c'est vraiment nécessaire.

    Je rappelle aussi qu'en maths, une fonction associe à chaque élément d'un domaine un élément du codomaine. Si on veut passer plusieurs paramètres, le domaine est un ensemble produit et on doit créer un tuple pour passer les paramètres. Dans beaucoup de langages ce n'est pas un problème, mais j'imagine que pour un langage très matheux comme Haskell c'est une source de complexité supplémentaire (en pratique on devra écrire add (a,b) au lieu de add a b, c'est un peu plus lourd mais ça reste acceptable, la principale raison reste idiomatique).

    Avec une syntaxe assez simple, il serait possible de faire la même chose: si _ est un argument a remplir plus tard, il est possible de faire f(x, _ , y).

    Il y a des langages qui proposent ce genre de syntaxe ? Est-ce que c'est vraiment plus clair qu'une syntaxe du genre z => f(x, z, y).

  • [^] # Re: Haskell super expressif ?

    Posté par  . En réponse au journal Comprendre Go en 5 minutes, en Haskell. Évalué à 4.

    Je ne comprends pas pourquoi les formes curryfiées seraient nécessaires pour avoir des fonctions pures. Une fonction de type A × B → C peut tout autant être pure que A → B → C.

    Pour moi, l'intérêt des fonctions curryfiées était avant tout l'application partielle.


    Pour la lecture, une fois qu'on me l'a expliqué, ça ne m'a jamais dérangé. Le prérequis important me semble de savoir dans quel ordre les flèches doivent être lues (elles ne sont pas associatives).
    A → B → C est A → (B → C) et non (A → B) → C. Le type du retour est donc derrière la dernière flèche, les autres flèches séparent les arguments dans l'ordre dans lequel on doit les donner. Si on veut retourner plusieurs objets, on est obligé d'utiliser un produit : A → B × C.

  • [^] # Re: Pfff ces informaticiens!

    Posté par  . En réponse au journal Blagues Friday. Évalué à 8.

    Le vrai mathématicien: 1 faux, c'est donc absolument faux.

  • [^] # Re: Yoda ?

    Posté par  . En réponse à la dépêche Python 3.8 : opérateur d’assignation, REPL async, Pickle v5 et plus. Évalué à 8.

    J'avais bien compris ça, mais c'est étrange comme nom quand on y pense. Yoda dirait plutôt "valeur" mavar == ("À valeur ma variable est égale"), non ?

  • [^] # Re: Z80, le meilleur processeur post-apo ?

    Posté par  . En réponse au journal Collapse OS : un système d’exploitation pour rebâtir la civilisation. Évalué à 6.

    En lisant quelques commentaires au hasard sur le post reddit. J'y ai vu quelques arguments en faveur de la simplicité plus convaincant que le nombre de transistors. Je retiens surtout (bien que je n'ai pas les connaissances techniques pour les confirmer) :

    • Les transistors plus petits des CPUs modernes ont une durée de vie limitée. Quelques décennies après la fin de la fabrication, ils cesseront de fonctionner.
    • Les circuits autour des CPUs anciens sont plus simples. Il y donc plus facile de trouver et remplacer les composants défectueux ou de les modifier pour changer l'utilisation de l'appareil.

    Ça veut dire qu'on se place dans un scénario où on est incapable de produire de l'électronique complexe pendant une très longue période, tout en continuant à produire des composants plus simples pour créer ou réparer des cartes autour de ces z80/6502 increvables. J'ai du mal à y croire : au bout d'un moment, soit tu arrêtes l'électronique, soit tu relances l'usine de circuits intégrés.

    Mais ça reste des questions intéressantes sur la durée de vie de l'électronique et sa réutilisation.

  • # Z80, le meilleur processeur post-apo ?

    Posté par  . En réponse au journal Collapse OS : un système d’exploitation pour rebâtir la civilisation. Évalué à 5.

    J'ai du mal avec l'argument de la simplicité : 9000 transistors, c'est peu mais ça reste quelque chose que j'imagine mal fabriquer avec mes petites mains. Donc ce qui compte c'est ce qui est le plus répandu. Est-ce que l'ARM ne battrait pas le Z80 avec l’inondation de smart-phones autre périphériques « intelligents » ?

  • [^] # Re: Vérification

    Posté par  . En réponse au journal 42 reste introuvable !. Évalué à 5. Dernière modification le 24 avril 2019 à 16:23.

    Si on prends les valeurs à la source, ça marche. La faute est dans la valeur de x du journal (8 866 128 9 8 5 287 528).

    x=int("8866128975287528")
    y=int("-8778405442862239")
    z=int("-2736111468807040")
    pow(x,3)+pow(y,3)+pow(z,3)

    donne 33.

  • [^] # Re: Ce n'est pas

    Posté par  . En réponse à la dépêche Minetest 5.0.0. Évalué à 2.

    Ceux qui jouent à DF ne devraient pas installer ce mods parce que ça risque de leur dévoiler une partie des surprises.

    Quelles surprises ? Celles que les joueurs de DF connaissent déjà ? Rien ne sera dévoiler dans ce cas. Je ne comprends pas cet avertissement.

  • [^] # Re: Expressivité

    Posté par  . En réponse au journal Les 7 étapes pour devenir un programmeur Go.. Évalué à 4.

    En fait, ça doit être mieux comme ça :

    for (auto &[k, v]: d)
        std::swap(v.first, v.second);

    Et en python, on ne peut pas modifier directement la paire ?

  • [^] # Re: Expressivité

    Posté par  . En réponse au journal Les 7 étapes pour devenir un programmeur Go.. Évalué à 5.

    d[it.first]

    Tu fais une recherche dans la map à chaque itération. C'est inutilement complexe. Tu devrais utiliser l'itérateur directement.

    En C++ moderne, c'est :

    for (auto &[k, v]: d)
        v = {v.second, v.first};
  • # Brevets concernés ?

    Posté par  . En réponse au journal Vers une fin de la guerre des brevets logiciels ?. Évalué à 10. Dernière modification le 10 octobre 2018 à 22:16.

    Bradley Kuhn (SFC) ne semble pas très enthousiaste. Les conditions pour qu'un brevet soit concerné par l'OIN semblent trop stricts. Il cite l'exemple de l'exFAT qui ne serait pas à l'abri d'attaques tant que Microsoft ne l'aura pas intégré à Linux.

  • [^] # Re: Ça pique les yeux

    Posté par  . En réponse au journal Mémorisation partielle de fonction constexpr. Évalué à 6. Dernière modification le 26 septembre 2018 à 18:13.

    Par exemple si j'écris 2 fois la ligne

    memoized<prime_sieve, uint64_t, 0,1,2,3,4,5,6,7,8,90> ps1;
    memoized<prime_sieve, uint64_t, 0,1,2,3,4,5,6,7,8,90> ps2;
    

    Est-ce qu'il va stocker 2 fois les résultats? Si ces lignes sont dans 2 librairies différentes, que ce passe t-il? (bien sûr qu'il va stocker 2 fois les résultats… )

    Le cache est statique, c'est donc la même variable partagée par tous les appels à une même fonction. Dans le cas où les types memoized sont identiques, le cache n'est pas recalculé. Mais si les types sont différents, les fonctions sont différentes, et donc, même avec des valeurs communes, le cache est complètement recalculé :

    memoized<prime_sieve, uint64_t, 0,1,2,3,4,5,6,7,8,90> ps1;
    memoized<prime_sieve, uint64_t, 0,1,2,3,4,5,6,7,8,90,91> ps2;
    

    ne partagent pas le même cache.

  • [^] # Re: Godbolt

    Posté par  . En réponse au journal Le quiz c++ de l'été. Évalué à 2.

    Pour garder un exemple dans le style du journal, mais où on sait ce que font les fonctions : https://gcc.godbolt.org/z/2llMlg

    C::f et C::g garde toutes les deux une copie du pointeur, mais f prend par référence et g par valeur. C::h prend possession du pointeur passé par référence de rvalue.

    • c.f(ptr_A) : le pointeur passé par référence puis copié dans la fonction -> 1 copie.
    • c.f(ptr_B) : le pointeur est converti, puis le temporaire est passé par référence, copié et détruit -> 2 copies, 1 destruction.
    • c.g(ptr_A) : le paramètre est crée par le constructeur de copie puis est déplacé par la fonction -> 1 copie, 1 déplacement, 1 destruction d'un pointeur nul (moins cher).
    • c.g(ptr_B) : pareil que le précédent sauf que le constructeur de copie est remplacé par la conversion.
    • c.h(std::move(ptr_A)) : passage par référence, puis affectation -> 1 déplacement.
    • c.h(std::move(ptr_B)) : conversion, passage par référence, puis affectation -> 2 déplacement, 1 destruction d'un pointeur nul.

    J'espère que c'est plus clair. Le passage par référence nécessite une copie supplémentaire pour la conversion, alors que le passage par valeur permet de limiter à une seule copie dans tous les cas.

  • [^] # Re: Un temporaire en plus

    Posté par  . En réponse au journal Le quiz c++ de l'été. Évalué à 1.

    si g n'a pas besoin de posséder la variable ET qu'elle est forcément définie alors un passage par référence est largement préférable

    Mais une référence de type A dans ce cas, la référence vers un shared_ptr ne garantie pas que le pointeur lui-même est déréférençable.

  • # Un temporaire en plus

    Posté par  . En réponse au journal Le quiz c++ de l'été. Évalué à 6.

    Les trois premiers appels se contentent de passer un pointeur (une référence c'est pointeur caché) et sont donc aussi rapides l'un que l'autre. g(shr_b) a besoin de créer un temporaire de type shared_ptr<A> et une copie de shared_ptr est coûteuse à cause de l'état partagé.

    En pratique, si g n'a pas besoin de posséder la ressource, il faut mieux utiliser pointeur nu ou un weak_ptr. Si g a besoin de la posséder, il faut mieux passer le shared_ptr par valeur: il y a une copie au moment de l'appel dans tous les cas, mais on peut le déplacer par le suite si besoin.