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

C++ Discussion :

template expression, foncteurs et static_assert/enable_if


Sujet :

C++

  1. #1
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut template expression, foncteurs et static_assert/enable_if
    Bonjour à tous!

    Ma question est assez simple à formuler: Comment valider les paramètres (de types) d'un operateur + combinant deux fonctions en une seule, dans le cadre d'expression template.

    Mon objectif est d'avoir une template de la forme suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename F1, typename F2>
    auto operator+(F1 f1, F2 f2) {
        static_assert(F1 et F2 s'appelle avec les mêmes arguments);
        static_assert(F1 et F2 produisent des types additionnables);
        return my::plus<F1, F2>{f1, f2};
    }
    La difficulté étant que la template doit pouvoir être instanciée pour plusieurs signatures, par exemple, deux fonctions int(int) ou deux fonctions double(double, double).
    J'essaie d'avoir un message clair si les types ne sont pas des fonctions/foncteurs compatible.
    Par contre, je peux certainement rajouter des enable_if pour m'assurer d'avoir des foncteurs/fonctions, comme types. Cependant qu'en sera-t-il des lambda généralisées?

    A priori, j'ai raté une astuce dans <type_trait>.
    Quel serait le mécanisme pour vérifier qu'une expression est valide? un équivalent de is_compilable(f1+f2).

  2. #2
    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
    Salut,

    Pour le premier cas tu peux vérifier si deux types sont identiques à l'aide de std::is_same
    Pour le deuxième cas, si tu te limites à l'addition "mathématique" (comprend : que tu ne voudras par exemple pas pouvoir utiliser std::string qui présente un opérateur +), tu peux te tourner vers std::is_arithmetic
    Au final (et pour autant que j'ai bien compris l'idée du deuxième cas), tu devrais pouvoir t'en sortir avec un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <typename F1, typename F2>
    auto operator+(F1 f1, F2 f2) {
        static_assert(std::is_same<F1, F2>::value, 
                             "first and second parameters should be same type");
        static_assert(std::is_arithmetic<F1>,
                             "parameters should be arithmetics type");
        return my::plus<F1, F2>{f1, f2};
    }
    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

  3. #3
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Je n'ai pas été très clair: F1 et F2 sont deux types de foncteurs.

    Par exemple, on pourrait avoir ce code-ci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    auto f = [](int x) {return -x;};
    auto g = [](int x) {return 2*x;} + [](int x) {return x+1;};//g équivalente de [](x){return (2*x)+(x+1);}
    auto h = f + [](int x) {return -x;};
    Avec la technique expression template, h aurai pour type plus< lambda_f, plus<lambda_1, lambda_2> >.
    J'ai une version un peu moins optimale à base de std::function, que j'optimiserai par la suite.

    Ce que j'essaie d'avoir, c'est une meta qui permette (dans mon exemple) de vérifier que les deux lambdas sont compatibles (pour produire un opérateur qui convient).
    Le soucis étant d'avoir une erreur de compilation non pas sur l'appel de my::plus::operator(), mais à la construction h = f+g.

    std::is_same n'est pas satisfaisant dans ce cas là, car en effet, les types de f et g sont compatible.
    Qui plus est, je risque d'avoir le même probleme avec la composition de fonctions (f(g))(x).

  4. #4
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Une idée, non testée, n'est-il pas possible de jouer avec SFINAE + decltype pour détecter une situation qui ne compile pas au plus tôt ?

    PS: attention avec les expressions template et auto et autre mémorisation. Il faut reconnaitre certains cas pour ne pas prendre de référence dessus, mais dupliquer ce qui est derrière.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  5. #5
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    Voici ce que je propose, sachant que cela ne permet pas de résoudre les cas où il y a une ambigüité intrinsèque, c'est-à-dire lorsque les types que tu additionnes sont des foncteurs qui surchargent l'opérateur de fonction:
    - Une structure Callable_traits permettant de récupérer le type des arguments (stockés dans un tuple) et le type de retour. Il manque des spécialisations: pointeurs de fonction, et un certain nombre de surcharges const/non const, etc. A toi de polir l'outil...
    - utiliser is_same sur les types d'arguments récupérés par la structure (donc stockés dans le tuple)
    - et SFINAE c++11 style pour l'addition:

    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
    36
    37
    38
    39
    40
    41
    42
    #include <iostream>
    #include <tuple>
    #include <type_traits>
     
    template <typename Callable>
        struct Callable_traits : public Callable_traits<decltype(&Callable::operator())> {}; // on passe le type de l'opérateur de fonction
    template <typename Ret, typename... Args>
        struct Callable_traits<Ret(Args...)> { // le cas de la fonction
            using Return_type = Ret;
            using Arguments_type = std::tuple<Args...>; // le tuple de stockage
        };
    template <typename Ret, typename Class, typename... Args>
        struct Callable_traits<Ret(Class::*)(Args...) const> { // le cas de la fonction membre, qui récupère donc l'opérateur de fonction
            using Return_type = Ret;
            using Arguments_type = std::tuple<Args...>;
        };
     
    template <typename A, typename B>
    constexpr auto is_additionable_impl(int) -> decltype( std::declval<A>() + std::declval<B>(), bool() ) { // ici la magie decltype
        return true;
    }
    template <typename A, typename B>
    constexpr bool is_additionable_impl(...) {
        return false;
    }
    template <typename A, typename B>
    constexpr bool is_additionable(){ // on recoure SFINAE pour faire plus joli
        return is_additionable_impl<A, B>(0);
    }
     
     
    int main() {
        auto l1 = [](int i) { return i * 2; };
        auto l2 = [](int i) { return i + 1; };
        using L1_info = Callable_traits<decltype(l1)>;
        using L2_info = Callable_traits<decltype(l2)>;
        std::cout << std::boolalpha;
        std::cout << std::is_same<L1_info::Arguments_type, 
                                  L2_info::Arguments_type>::value; // true
        std::cout << std::endl;
        std::cout << is_additionable<L1_info::Return_type, L2_info::Return_type>(); // true
    }
    EDIT: Il faudrait peut-être réfléchir à employer common_type plutôt que is_same, mais d'après la doc il n'est garanti "SFINAE friendly" qu'à partir de C++17. Donc à tester...

  6. #6
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Ca m'a tout l'air de correspondre, en effet.
    Je vais aussi essayer grouper les déclarations de is_additionnable et des futures is_substractable, is_divideable, is_multipliable, is_moduloable, is_composable en utilisant un foncteur en parametre (std::plus, std::minus, etc)

    Je fermerai dès que j'aurai testé.

  7. #7
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Super, avec quelques petits réglages, ca marche du tonnerre.

    Voici mon opérateur plus (et le plus_t qui va avec):
    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    template <typename F1, typename F2>
    struct plus_t {
    	using F1_info = Callable_traits<F1>;
    	using F2_info = Callable_traits<F2>;
    	using value_type = decltype( std::declval<typename F1_info::Return_type>() + std::declval<typename F2_info::Return_type>() );
    	F1 f1;
    	F2 f2;
    template< class Expr >
     
    	plus_t(F1 f1, F2 f2) : f1(f1), f2(f2) {
    		static_assert(
    			std::is_same< typename Callable_traits<F1>::Arguments_type, typename Callable_traits<F2>::Arguments_type >::value,
    			"incompatible functors domains"
    		);
    		static_assert(
    			is_additionable< typename Callable_traits<F1>::Return_type, typename Callable_traits<F2>::Return_type >(),
    			"incompatible functors values"
    		);
    	}
     
    	template <typename... Args>
    	value_type operator()(Args... args) const {
    		static_assert(
    			std::is_same<
    				typename Callable_traits<F1>::Arguments_type,
    				typename Callable_traits<void(Args...)>::Arguments_type
    			>::value,
    			"wrong arguments type or count"
    		);
    		return impl(args...);//use a sub function to shut up compiler, we already have an assert
    	}
    private:
    	template <typename... Args>
    	value_type impl(Args... args) const {
    		static_assert(
    			std::is_same<
    				typename Callable_traits<F1>::Arguments_type,
    				typename Callable_traits<void(Args...)>::Arguments_type
    			>::value,
    			"invalide operator call"
    		);
    		return f1(args...) + f2(args...);
    	}
    };
     
    template <typename F1, typename F2>
    auto operator+(F1 f1, F2 f2) {return plus_t<F1, F2>{f1, f2};}
    Le code de test est le suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main() {
        auto l1 = [](int i) { return i * 2; };
        auto l2 = [](int i) { return i + 1; };
        auto l3 = [](double i) { return i + 1; };
     
    	auto a = l1 + l2;
    	std::cout << "adding 3*2 and 3+1: " << a(3, 2) << std::endl;
     
    	auto b = l1 + l3;
    }
    Je n'ai en effet que des messages d'assert, sans tout une pollution de templates ininstanciables:

    g++ -std=c++1y -O2 -Wall -Wextra test_asserting.cpp -o test && ./test 
    test_asserting.cpp: In function ‘int main()’:
    test_asserting.cpp:115:7: warning: variable ‘b’ set but not used [-Wunused-but-set-variable]
      auto b = l1 + l3;
           ^
    test_asserting.cpp: In instantiation of ‘math::expressive::plus_t<F1, F2>::plus_t(F1, F2) [with F1 = main()::<lambda(int)>; F2 = main()::<lambda(double)>]’:
    test_asserting.cpp:100:59:   required from ‘auto math::expressive::operator+(F1, F2) [with F1 = main()::<lambda(int)>; F2 = main()::<lambda(double)>]’
    test_asserting.cpp:115:16:   required from here
    test_asserting.cpp:64:3: error: static assertion failed: incompatible functors domains
       static_assert(
       ^
    test_asserting.cpp: In instantiation of ‘math::expressive::plus_t<F1, F2>::value_type math::expressive::plus_t<F1, F2>::operator()(Args ...) const [with Args = {int, int}; F1 = main()::<lambda(int)>; F2 = main()::<lambda(int)>; math::expressive::plus_t<F1, F2>::value_type = int; typename math::expressive::plus_t<F1, F2>::F1_info::Return_type = int; typename math::expressive::plus_t<F1, F2>::F2_info::Return_type = int]’:
    test_asserting.cpp:116:47:   required from here
    test_asserting.cpp:76:3: error: static assertion failed: wrong arguments type or count
       static_assert(
       ^
    
    Cependant, pour pouvoir combiner les operateurs ensembles, pour former des opérations plus complexes, il faut surcharger Callable_traits pour plus_t.
    Au passage, il ne dois pas être compliqué de n'avoir qu'une seule classes pour tous les opérateurs mathématiques, il suffirait d'utiliser std::plus<void> et ses frères.

    Vive le C++14

  8. #8
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    Juste une question que je me pose: comme, avant le constructeur qui contient les static_assert, tu as déjà:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using value_type = decltype( std::declval<typename F1_info::Return_type>() + std::declval<typename F2_info::Return_type>() );
    comment se fait-il que l'erreur de compilation ne soit pas détectée à ce moment-là et génère les habituelles tornades de messages incompréhensibles? Peut-être faudrait-il étendre tes cas de tests à une lambda qui retourne un type non additionable:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    // comme avant
    auto l4 = [](int i) { return std::string(); };
    auto l5 = l1 + l4; // que se passe-t-il?

  9. #9
    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
    En gros, c'est parce que tu as un truc très sympa avec les template qui s'appelle SFINAE (Substitution Failure Is Not An Error ou "l'échec d'une substitution ne constitue pas une erreur"):le compilateur attend la toute dernière limite, d'avoir vraiment testé toutes les possibilités avant de te cracher l'erreur avec toutes les possibilités qu'il a testé .

    C'est d'ailleurs pour cela que les messages sont particulièrement indigestes : le compilateur te bazarde toutes les expressions templates qu'il a pu tester avec toutes les combinaisons de paramètres template qu'il a essayé.

    Or, il faut savoir que, dans bien des cas, la première combinaison indiquée aurait normalement pu fonctionner car le compilateur est particulièrement bon au jeu qui consiste à déterminer de quel type est une donnée particulière.

    Static_assert "court-circuite" le fonctionnement par défaut du compilateur que je viens de décrire en disant "la condition indiquée n'est pas validée, tu envoies ce message (et rien d'autre)".

    Bien sur, je simplifie énormément les choses (rien que SFINAE mérite une explication bien plus longue, et l'explication du fonctionnement de static_assert nécessite quelques pages dans la norme ), mais c'est en gros l'idée générale
    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. #10
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    En gros, c'est parce que tu as un truc très sympa avec les template qui s'appelle SFINAE (Substitution Failure Is Not An Error ou "l'échec d'une substitution ne constitue pas une erreur"):
    Je connais plutôt bien SFINAE -j'ai écrit un article dessus, pour tout dire- mais je ne pense pas que cela explique la question. SFINAE veut dire que le compilateur cherchera à instancier une autre classe/fonction plutôt que de s'arrêter à la première instanciation fautive qu'il rencontre. Mais dans ce cas précis, il n'y a pas d'instanciation alternative de plus_t (pas dans le code que le ter Nel nous a montré, en tout cas); donc pas de SFINAE non plus. Pourquoi l'erreur dans le constructeur serait-elle retenue (alors qu'elle est postérieure) plutôt que celle détectée dans la directive using qui le précède?
    Ce n'est d'ailleurs peut-être pas le cas puisque les données de test ne contenaient pas le cas où l'échec est lié à l'opérateur +.

  11. #11
    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
    Parce que, justement, le static_assert représente une instruction issue du développeur lui-même, c'est à dire, de nature à supplanter tous les comportements "par défaut" que l'on peut avoir définis au niveau du compilateur.

    Tu sais que le compilateur fait énormément de choses "tant que l'on ne lui dit pas de ne pas le faire"; comme -- c'est l'exemple le plus frappant -- de fournir une implémentation "par défaut" du constructeur de copie, de l'opérateur d'affectation et du destructeur.

    Mais la décision du développeur peut modifier ce comportement, soit parce que l'on donne notre propre définition de ces fonctions, soit parce que l'on en défini l'une ou l'autre comme étant delete. Et, quoi qu'il en soit, le compilateur se pliera à notre volonté parce que "le développeur est sensé savoir ce qu'il fait".

    Hé bien, c'est exactement le même principe ici : SFINAE permet d'arriver jusqu'au niveau du constructeur en ayant déterminé le bon type associé aux différents paramètres template. Mais, alors que le compilateur aurait sans doute accepté les types en question sans se poser de question on lui dit : mais attention, il y a une condition pour que cela soit correcte" et, vu que cette condition est énoncée par le développeur, le compilateur n'a pas d'autre choix que de la respecter.

    Du coup, si la condition en question n'est pas respectée, le compilateur n'a plus d'autre choix que de faire ce que l'on attend de lui dans ce genre de cas, à savoir: sortir sur un code d'erreur après avoir affiché le message indiqué. C'est aussi simple que cela
    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

  12. #12
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    @koala01: Je pense que ce que veut savoir stendhal666. c;est pourquoi le typedef sur value_type ne pète pas d'erreur alors que celui-ci est dans le scope de la classe (donc avant celui du constructeur).

    Réponse: Parce que value_type n'est pas utilisé dans la définition de plus_t et n'est donc pas évalué. Ce qui s'applique aux fonctions s'applique aussi aux membres: les erreurs n'apparaissent qu'à la première utilisation.

  13. #13
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Tout particulièrement parce que, faisant partie d'une template, le using n'est même pas compilé tant qu'on ne s'en sert pas, c'est à dire avant l'instanciation de l'opérator().

    Je continue à travailler là chose, je pense que ca se transformera en bibliothèque prochainement.
    J'en suis à la composition de fonctions (le chainage de fonction à un argument est fait, reste la composition à plusieurs arguments).

  14. #14
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    Si tu continues sur ce chemin tu vas finir par rencontrer les monades

  15. #15
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Ce qui risque de poser problème, parce que je n'ai toujours pas compris les mathématiques correspondantes

    J'espère aussi ne pas avoir à me tourner vers les dérivées (partielles?) et intégrales.

    Fait amusant, ma nouvelle version de la bibliothèque permet d'envoyer sur un ostream les expressions créées via les opérateurs mathématiques, si les sous-expressions sont affichables.
    C'est le cas dans le programme cible, un code comme cout << environnement["pluie"] + environnement["température"] << endl; affichant: "pluie(x,y,t) + température(x,y,t)".

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 15
    Dernier message: 02/07/2007, 08h23
  2. Foncteur, classes templates et héritage
    Par Floréal dans le forum C++
    Réponses: 8
    Dernier message: 17/06/2007, 21h56
  3. Problème expression templates
    Par Bakura dans le forum C++
    Réponses: 5
    Dernier message: 06/06/2007, 20h08
  4. [Débutant]Foncteur et operator() template
    Par Sub dans le forum Langage
    Réponses: 14
    Dernier message: 27/03/2007, 13h51
  5. Réponses: 12
    Dernier message: 27/01/2007, 12h32

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