Partage
  • Partager sur Facebook
  • Partager sur Twitter

Types opaques: Domaines de définitions

    19 octobre 2016 à 13:25:40

    Il y a quelques jours, j'avais expérimenté comment disposer d'un type opaque pour avoir des domaines de définition. Au départ je comptais intégrer la bête dans ma série sur la PpC.

    Suite à un post sur reddit faisant de la pub pour une lib sur le sujet des (sur-)types opaques (non_null, negative, sorted, ...), j'ai posté mon expérience.

    Je suis preneur de retours ou d'idées pour ne pas avoir à devoir faire des pirouettes telles que

    domain<double, decltype(-1.57079_c), decltype(1.57079_c)> asin(domain<double, decltype(-1_c), decltype(1_c)>);

    alors que l'on voudrait exprimer plus simplement:

    domain<double, -pi/2, +pi/2> asin(domain<double, -1, +1>);


    Ah! L'objectif? Faire en sorte que l'on ne puisse pas passer à arc-sinus un nombre qui n'est pas dans la plage [-1, +1], et faire en sorte que le compilateur nous envoie bouler quand on oublie de le garantir explicitement (par construction convertissante) ou par chainage d'appels.

    -
    Edité par lmghs 19 octobre 2016 à 15:01:54

    • Partager sur Facebook
    • Partager sur Twitter
    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
      19 octobre 2016 à 13:47:48

      Lu'!

      J'ai pas d'idée particulière pour éviter cela. Cependant, je trouve ça rigolo, parce que j'avais commencé à réfléchir à un truc du genre pour faire un calcul de plus faible pré-conditions en utilisant les templates. Bon ce serait atrocement sale parce qu'il faudrait redéfinir toutes les constructions du langage, y compris les structures de contrôle mais l'idée m'amuse.

      • Partager sur Facebook
      • Partager sur Twitter

      Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

        19 octobre 2016 à 13:51:16

        je dirais decltype(domain<double>(-pi/2_c, +pi/2_c)). Je suppose qu'avec c++17 et les types auto, on peut faire exactement ce que tu veux. Par contre double ne passe pas en paramètre et je ne vois pas comment le contourner. C'est la raison de la virgule dans -1,57079_c ?

        • Partager sur Facebook
        • Partager sur Twitter
          19 octobre 2016 à 15:00:46

          @jo_link_noir, Non, la virgule doit venir de mon utilisation du point dans le pavé numérique. Je vais corriger ça.

          Après, ce que je cherche à définir, c'est un type qui me génère à la volée d'autres types ; i.e. histoire que je puisse ajouter/multiplier/.. des domaines de définition. Idéalement, j'aurai aimé éliminer toute occurrence de decltype. Là, je sens que c'est très compliqué.

          @Ksass`Peuk, Tu veux redéfinir les structures de contrôle? Tu veux dire les if, while & cie ? Pour le coup, je ne vois pas le cas d'utilisation auquel ça correspondrait. Je me serai contenté de juste redéfinir les opérateurs arithmétiques et de comparaison.

          • Partager sur Facebook
          • Partager sur Twitter
          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
            19 octobre 2016 à 15:06:44

            lmghs a écrit:

            @Ksass`Peuk, Tu veux redéfinir les structures de contrôle? Tu veux dire les if, while & cie ? (1) Pour le coup, je ne vois pas le cas d'utilisation auquel ça correspondrait. (2) Je me serai contenté de juste redéfinir les opérateurs arithmétiques et de comparaison.

            (1) Aucun. C'est juste que les prouveurs interactifs comme Coq sont basés sur le typage. Et je pense que ce serait rigolo de faire la même chose avec les templates.

            (2) Si je veux faire un WP, il faut que je puisse prendre en compte les conditions des structures de contrôles pour enrichir correctement les propriétés en remontant le code. C'est plus bourrin.

            • Partager sur Facebook
            • Partager sur Twitter

            Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

              19 octobre 2016 à 17:51:14

              @lmghs: je pense à une solution pour enlever le decltype, mais elle a son lot de défauts. Il faudrait que domain prenne un T et 2 unsigned long long, et que les variables du type de pi soient convertibles en unsigned long long et mappé sur la représentation interne d'un double. Pour ensuite faire l'inverse ull -> double si T est bien un double.

              Les entiers très gros pourraient être confondus avec un double, mais je ne pense pas que ça soit grave.


              Redéfinir les structures de contrôle, je le fais en ce moment au boulot. Le but est de décrire les paquets d'un protocole réseau pour ensuite attacher des variables et lire la liste de paquets pour les écrire (en calculant la taille et le nombre de buffer nécessaire si aucun buffer n'est donné). À terme, avoir un algo pour écrire des logs, faire la lecture de paquet (en ignorant certaines valeurs si on veut), avoir un parseur texte -> paquet (pour des programmes de test) et tout ce qui peut passer par la tête. Le tout à partir d'une même description.

              Pour le moment ça donne: https://gist.github.com/jonathanpoelen/746e01de3665b458e2d3c1238e75683d#file-proto_example-cpp-L90 (voir au-dessus pour la "description" d'un paquet).

              Il y a des variables nommées, des dsl, des variables magiques (calcul automatique de la taille des paquets ou réécriture des données déjà écrite) de la vérification de type à la compilation pour éviter les conversions foireuse (u16 -> u8).

              Il n'y a pour le moment que if et else (pas de if else, ni de boucle) et je fais un type optional_sequence où les valeurs sont optionnelles, mais toutes celles à gauche d'une valeur écrite doivent exister (seq(a,b,c) -> ok ; seq(a, ignore_t{}, c) -> erreur de compilation ; seq(a, maybe(false, b), maybe(true, b)) -> erreur d’exécution)

              Je peine beaucoup :)

              -
              Edité par jo_link_noir 19 octobre 2016 à 23:09:01

              • Partager sur Facebook
              • Partager sur Twitter
                20 octobre 2016 à 1:12:14

                L'idée de mapper des doubles en leur représentation entière me plait. Mais après expérience, je découvre que reinterpret_cast ne s'utilise pas dans des fonctions constexpr, pas plus que les unons on dirait.

                Une piste serait de calculer un encodage IEEE (réversible) à la main.

                • Partager sur Facebook
                • Partager sur Twitter
                C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                  20 octobre 2016 à 3:09:40

                  Les unions fonctionnent, il faut mettre un constructeur constexpr qui initialise une variable. Le problème c'est que la technique habituelle à base d'union est en réalité une UB, on ne peut pas utiliser un autre membre que celui initialisé.

                  reinterpret_cast peut être remplacé par 2 static_cast avec un passage par un void pointeur (j'ai eu le coup avec des pointeurs char -> uchar et clang (gcc en a absolument rien à faire)). Après test, ça ne passe pas avec double :/.

                  Si tu fais l'encodage/décodage, je suis intéressé.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    20 octobre 2016 à 12:02:35

                    J'avais essayé le coup des casts et clang ne laisse pas passer.

                    A part écrire une fonction d'encodage à la IEEE des doubles pour fabriquer des entiers 64 bits, je ne vois pas trop -- vu que ce n'est pas trop prioritaire je mets en pause pour l'instant. Après on pourrait écrire des choses comme:

                    using D1 = domain<double, bound::close, bound::open, 12.5_c, bound::tpl(150 * pi<double>)> ;
                    
                    // et soyons fou!
                    template <typename T, std::uint64_t min_, std::uint64_t max_>
                    using close_domain<double, bound::close, bound::close, min_, max_>; 
                    
                    template <typename T, std::uint64_t min_, std::uint64_t max_>
                    using left_open_domain<double, bound::open, bound::close, min_, max_>; 
                    ...


                    Gérer les infinis avec des bornes comme `bound::infinte`, ...

                    • Partager sur Facebook
                    • Partager sur Twitter
                    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                      21 octobre 2016 à 14:31:05

                      Salut,

                      On ne peut pas utiliser de type flotant pour les valeurs des templates, ce qui va rendre ton problème vraiment compliqué. Il y a bien moyen de passer des fractions, mais dès que tu va avoir un irrationnel (genre pi) tu es cuit.

                      De manière plus fondamentale, même en supposant que de la magie te permette d'utiliser des flotants, ca ne va pas marcher. Les flotants sont des approxilations, et les preuves dont tu as besoin sont exactes.

                      Si tu veux faire des approximations à l'exécution, va voir boost::intervall, je n'ai hélas rien de mieux

                      • Partager sur Facebook
                      • Partager sur Twitter
                        21 octobre 2016 à 14:51:17

                        Il est clair que rien qu'avec pi et ses fractions, on arrive vite à avoir des intervalles irrationnels. Et pour l'instant, au mieux mes feintes bossent avec des approximations.

                        Pour le coup, je ne me sens pas de me lancer à définir un langage de calcul formel (en méta-prog template + constexpr) capable de manipuler des constantes abstraites (telles que pi) qui soit capable de réduire les expressions, ou du moins de les comparer avec < et avec =.

                        • Partager sur Facebook
                        • Partager sur Twitter
                        C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                          25 novembre 2016 à 1:43:39

                          Plop,

                          Je remonte le sujet pour proposer une idée de passage et éviter l'utilisation de decltype.

                          Actuellement 123.456_c crée un std::ratio. Je pense que faire sa propre classe ratio permettra d'ajouter un cast implicite vers un uint64_t.

                          la transformation du ratio peut se faire dans une version simple en utilisant 32 bits pour la partir entière et 32bits pour la partie décimal. Ou une version plus complexe qui permet d'utiliser plus de bits pour la partie décimal:

                          % | nombre de 4 bits utilisé par la partie entière | partie entière | partie décimal bits | 2 | 4, 8, 12, 16 | 58, 54, 50, 46

                          domain pourrait ressembler à ça:

                          #include <cstdint>
                          
                          template<class T>
                          struct IntegralDomainImpl
                          { using type = T; };
                          
                          enum class FloatingPointIntegral : uint64_t {};
                          // FloatingPointIntegral operators
                          // ...
                          
                          template<>
                          struct IntegralDomainImpl<double>
                          { using type = FloatingPointIntegral; };
                          
                          template<class T>
                          using IntegralDomain = typename IntegralDomainImpl<T>::type;
                          
                          template<class T, IntegralDomain<T> min_, IntegralDomain<T> max_>
                          struct domain
                          {
                            //...
                          };
                          
                          // plus simple que ratio pour l'exemple
                          template<long long integral, unsigned long long decimal>
                          struct my_double
                          {
                            constexpr operator FloatingPointIntegral () const
                            {
                              // static_assert sur decimal et integral
                              // gaffe au signe dans les décalages
                              return static_cast<FloatingPointIntegral>((integral << 32) | decimal);
                            }
                          };
                          
                          constexpr auto pi = my_double<3, 14>{}; // 3.14_c
                          
                          domain<double, -pi/2, +pi/2> // ok
                          domain<double, -1, +1> // non puisque -1 et 1 ne sont pas des FloatingPointIntegral contrairement à 1._c
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Types opaques: Domaines de définitions

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown