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

Langage C++ Discussion :

Modificateur de fonction template


Sujet :

Langage C++

  1. #1
    Membre à l'essai
    Homme Profil pro
    Ingénieur géomaticien
    Inscrit en
    Juillet 2015
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur géomaticien

    Informations forums :
    Inscription : Juillet 2015
    Messages : 34
    Points : 24
    Points
    24
    Par défaut Modificateur de fonction template
    Bonjour à tous !
    Je suis nouveau sur ce forum donc n'hésitez pas à me reprendre et à me signaler si je ne respecte pas toutes les règles .

    J'utilise GCC version C++03. Je peux upgrader en C++11 ou 14 si nécessaire.

    Je me pose un problème assez général, plutôt ambitieux .

    • Je souhaite travailler avec des fonctions (fonctions ou foncteurs, à voir) template. Jusque là tout va bien :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      #include <vector>
       
      using namespace std;
       
      template<typename T>
      T add(const T& a, const T& b)
      {
          return a+b;
      }
    • Je souhaite maintenant travailler avec des fonctions d'ordre supérieur (un modificateur) qui prendrait une ou deux fonctions template et "renverrait" une autre fonction template dérivée. Par exemple "cumulate" prendrait la fonction "add" et renverrait "cum_sum". Un truc du style :
      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
      /**
      Le modificateur cumulate prend une fonction binaire (add, multiply...) sous forme de pointeur.
      Il renvoie une fonction unaire qui permet l'accumulation de cette fonction entre les éléments d'un vecteur passé en entrée.
      */
      template<typename T>
      T(*)(const vector<T>&) cumulate(  vector<T> (*base_func)(const vector<T>&, const vector<T>&)  )
      {
            //Définition de la fonction dérivée
            T derived_func( const vector<T>& V) {
                 result = V[0];
                 for(int i=1; i<V.size(); i++){
                      result = add<T>(result, V[i]);
                 }
                 return result;
            }
            return &derived_func;
      }


    J'ai conscience que le code ci-dessus ne peut pas compiler (mises à part mes erreurs de syntaxe ), car le compilateur doit pouvoir "fixer" les types de la fonction avant toute utilisation. Néanmoins, je souhaite vraiment ne dériver la fonction qu'une seule fois et pouvoir utiliser la fonction dérivée générique dans plein de contextes différents.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main(){
        template<typename T> // C'est louche...
        T(*)(const vector<T>&) cumsum = cumulate(add) // Aïe Aïe Aïe !!!
     
        vector<int> V_int(2) // 2 entiers
        vector<bool> V_bool(3) // 3 booléens
        vector<float> V_float(4) // 4 floattants
     
       /* ... Remplissage des vecteurs ... */
     
        cout << *cumsum<int>(V_int) << endl; // Somme cumulée des deux entiers
        cout << *cumsum<bool>(V_bool) << endl; // Somme cumulée des trois booléens (donne le nombre de prédicats VRAIS)
        cout << *cumsum<float>(V_float) << endl; // Somme cumulée des quatre flottants
    }
    J'imagine plein d'autres modificateurs comme la composition, la négation, la curryfication (binding)... Mais mon objet de base reste la fonction "templatisée" et je souhaite retarder la spécialisation au maximum, càd seulement lorsque cette fonction est appelée. De la méta-prog en somme ... Je suis déjà allé voir du côté de la STL qui propose ce genre de modificateurs sur les foncteurs (compose, bind...) mais là encore le foncteur doit être "spécialisé" avant de pouvoir passer comme argument du modificateur.

    Des idées ?

  2. #2
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Rapidement, j'ai une vague idée de ce que tu veux exactement ou de comment te proposer une implémentation. Mais regarde donc ça :
    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
    48
    49
    50
    51
    52
    #include <functional>
    #include <type_traits>
     
    #include <vector>
    #include <complex>
    #include <iostream>
    #include <typeinfo>
     
    namespace
    {
        struct FoldLeft
        {
    	template<typename _a, typename _b, typename Iterator>
    	_a operator()(std::function<_a(_a,_b)> &f, _a zero, Iterator begin, Iterator const& end)
            {
    	    _a acc = zero;
                Iterator it = begin;
     
                while (it != end) {
                    acc = f(acc, *it++);
                }
     
                return acc;
            }
        };
     
        int add(int a, double b)
        {
    	return a + b;
        }
     
        std::complex<int> mul(std::complex<int> a, int b)
        {
    	return a * b;
        }
    }
     
    int main(void)
    {
        std::vector<double> v{0.0,1.0,2.0,3.0};
        int a[] = {4,5,6,7,8,9,10};
        std::function<int(int,double)> fadd(add);
        std::function<std::complex<int>(std::complex<int>,int)> fmul(mul);
     
     
        auto r1(FoldLeft()(fadd, 0, v.begin(), v.end()));
        auto r2(FoldLeft()(fmul, std::complex<int>(0,1), a, a+3)); /* only 4, 5, 6 */
     
        std::cout << "result: type=" << typeid(r1).name() << " value=" << r1 << std::endl;
        std::cout << "result: type=" << typeid(r2).name() << " value=" << r2 << std::endl;
        return 0;
    }
    Compiler avec gcc gérant c++11 au minimum avec -std=c++11.
    -- Yankel Scialom

  3. #3
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    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 : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    En c++14 avec les foncteurs généralisés.

    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
    #include <functional>
    #include <algorithm>
     
    #include <vector>
    #include <complex>
    #include <iostream>
    #include <typeinfo>
     
     
    int main(void)
    {
      std::vector<double> v{0.0,1.0,2.0,3.0};
      int a[] = {4,5,6,7,8,9,10};
      std::plus<> fadd;
      std::multiplies<> fmul;
     
      auto r1(std::accumulate(v.begin(), v.end(), 0, fadd));
      auto r2(std::accumulate(a, a+3, std::complex<int>(0,1), fmul));
     
      std::cout << "result: type=" << typeid(r1).name() << " value=" << r1 << std::endl;
      std::cout << "result: type=" << typeid(r2).name() << " value=" << r2 << std::endl;
      return 0;
    }
    Le principe est de mettre la template sur la fonction operator() du foncteur et non plus sur la classe elle-même.

  4. #4
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Tiens, je ne connaissais pas std::accumulate ; c'est toujours triste de réinventer la roue.
    -- Yankel Scialom

  5. #5
    Membre à l'essai
    Homme Profil pro
    Ingénieur géomaticien
    Inscrit en
    Juillet 2015
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur géomaticien

    Informations forums :
    Inscription : Juillet 2015
    Messages : 34
    Points : 24
    Points
    24
    Par défaut
    Merci prgasp77 et jo_link_noir pour vos réponses.

    Citation Envoyé par jo_link_noir Voir le message
    En c++14 avec les foncteurs généralisés.
    Il y a-t'il une doc sur ce type de foncteurs (généralisés ? générique ?) ? En tout cas la simplification que ça apporte fait fremir.

    OK je suis clair sur l'utilisation du foncteur pour l'accumulation. Ce que je voudrais maintenant c'est que le résultat de l'opérateur () de FoldLeft (foncteur qui agit sur un autre foncteur comme fadd ou fmul) ne soit plus directement le résultat mais un foncteur aussi généralisé que le sont fadd et fmul. On retarde ainsi le passage du vecteur en paramètre. Dans le main ça donnerait :
    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
     
    // Définition des fonctions de base
    std::plus<> fadd;
    std::multiplies<> fmul;
     
    FoldLeft fl;
     
    std::function<> cumul_add = fl(fadd, 0);
    std::function<> cumul_mult= fl(fmul, 1);
     
     
    std::vector<double> v{1.0,2.0,4.0};
    std::vector<complex<int>> w{std::complex<int>(0,1),std::complex<int>(2,1),std::complex<int>(3,0)};
     
     
    std::cout << "result: type=" << typeid(cumul_add(v)).name() << " value=" << cumul_add(v) << std::endl;  // -> result type=d value=7.
    std::cout << "result: type=" << typeid(cumul_add(w)).name() << " value=" << cumul_add(v) << std::endl;  // -> result type=St7complexIiE value=(5,1)
     
    std::cout << "result: type=" << typeid(cumul_mult(v)).name() << " value=" << cumul_add(v) << std::endl;  // -> result type=d value=8.
    std::cout << "result: type=" << typeid(cumul_mult(w)).name() << " value=" << cumul_add(v) << std::endl;  // -> result type=St7complexIiE value=(-3,6)
    Est-il possible de réécrire l'opérateur () de FoldLeft de manière à obtenir le résultat escompté ?

  6. #6
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    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 : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Pour la doc, j'utilise cppreference: http://en.cppreference.com/w/cpp/utility/functional
    Si tu veux voir une implémentation possible: https://github.com/jonathanpoelen/fa.../operators.hpp

    En C++14 tu peux faire des trucs extrêmement concis avec les lambdas:

    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    #include <iterator>
    #include <algorithm>
     
    struct foldl_fn {
      constexpr foldl_fn() noexcept {}
     
      template<class Fn, class T>
      auto operator()(Fn && fn, T && start) const {
        return [fn,start=std::forward<T>(start)](auto && cont) {
          using std::begin;
          using std::end;
          return std::accumulate(begin(cont), end(cont), start, fn);
        };
      }
    };
     
    /* C++11 pour remplacer la lambda
     
    template<class Fn, class T>
    struct foldl_expr {
      Fn fn;
      T start;
     
      auto opearator()(Cont &&) const -> decltype(...) {
        ...
      }
    };
    */
     
     
     
    template<class T>
    struct static_const
    {
      static constexpr T value{};
      constexpr const T & operator()() const noexcept
      { return value; }
    };
     
    template<class T>
    constexpr T static_const<T>::value;
     
     
    #include <functional>
     
    namespace {
      constexpr auto const & foldl = static_const<foldl_fn>::value;
      constexpr auto const & plus = static_const<std::plus<>>::value;
    }
     
     
    #include <iostream>
     
    int main(){
      auto l = {1,2,3,4,5};
      auto cumul_add = foldl(plus, 0);
      std::cout << cumul_add(l) << '\n'; // 15
    }

  7. #7
    Membre à l'essai
    Homme Profil pro
    Ingénieur géomaticien
    Inscrit en
    Juillet 2015
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur géomaticien

    Informations forums :
    Inscription : Juillet 2015
    Messages : 34
    Points : 24
    Points
    24
    Par défaut
    Merci beaucoup !

    Faut que je potasse ça. Je comprends le principe du code et le pourquoi de l'utilisation des lambdas. Ça répond bien à ma question. Maintenant je dois m'imprégner de cette nouvelle syntaxe propre à C++ 14 afin de le comprendre complétement et de pouvoir faire fonctionner mes propres exemples. Je m'y attendais ! mais maintenant grâce à vous deux j'ai un point de départ.

  8. #8
    Membre à l'essai
    Homme Profil pro
    Ingénieur géomaticien
    Inscrit en
    Juillet 2015
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur géomaticien

    Informations forums :
    Inscription : Juillet 2015
    Messages : 34
    Points : 24
    Points
    24
    Par défaut Intérêt de static_const
    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
    template<class T>
    struct static_const
    {
      static constexpr T value{};
      constexpr const T & operator()() const noexcept
      { return value; }
    };
     
    template<class T>
    constexpr T static_const<T>::value;
     
     
    #include <functional>
     
    namespace {
      constexpr auto const & foldl = static_const<foldl_fn>::value;
      constexpr auto const & plus = static_const<std::plus<>>::value;
    }
    Je me demandais quel est la fonction exacte de static_const ? Est-ce ce qu'on appelle une classe de traits ? En effectuant des tests, j'ai remarqué que niveau utilisation, cela revenait à faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    namespace {
      constexpr auto const & foldl = foldl_fn();
      constexpr auto const & plus = std::plus<>();
    }
    à la différence près que foldl et plus sont des objets "anonymous", ce qui ne fait pas mon affaire...

    Quelle est la plus-value apportée par static_const ? la vitesse ?

  9. #9
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    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 : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    ODR (http://en.cppreference.com/w/cpp/language/definition)

    L'équivalent serait plus constexpr auto foldl = fold_fn();, mais je crois qu'il faut en plus ajouté static et cela à un effet sur le binaire (la variable n'est plus considérée comme une constante de compilation).
    À vérifier, notamment avec les changements sur constexpr apportés par C++14.

  10. #10
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 670
    Points
    10 670
    Billets dans le blog
    3
    Par défaut
    Dans un autre style, inspiré par cet article:
    https://chriskohlhepp.wordpress.com/...n-cplusplus14/

    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
    #include <iostream>
    #include <vector>
    #include <algorithm>
     
    using namespace std;
     
    auto add = [](auto a, auto b) {
        return a+b;
    };
     
    auto cumulate = [](auto fn) {
        return [fn](const auto & cont) {
           auto it = cbegin(cont);
           auto start = decltype(*it){};
           return accumulate(it, cend(cont), start, fn);
        };
    };
     
    int main(){
        auto v1 = {1, 2, 3, 4, 5};
        auto v2 = std::vector<double>{1.1, 2.2, 3.3, 4.4, 5.5};
     
        auto cumul_add = cumulate(add);
        cout << cumul_add(v1) << "\n";
        cout << cumul_add(v2) << "\n";
    }

  11. #11
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    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 : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Le problème des lambdas globales, c'est qu'elles brisent l'ODR. Entre 2 unités de compilation, l’adresse de add n'est pas là même et on peut se retrouver en déroulant un code avec &add != &add.
    L’exécutable est aussi plus gros.

    D'où le static_const + namespace anonyme.
    - static_const pour ne pas briser l'ODR car les membre statique d'une classe sont garantie unique pour toutes les unités de compilation.
    - namespace anonyme pour un linkage interne et ne pas avoir de multiple définition dans la phase de link.

  12. #12
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 670
    Points
    10 670
    Billets dans le blog
    3
    Par défaut
    Merci pour les explication sur l'utilité de static_const. Je comprends la solution, mais pas le problème. En quoi c'est différent d'une variable globale de type int?

    Je suppose que tu as conçu ton code pour être placé dans un .h, d'où ces précautions. Mais même dans ce cas, je trouve que cela rend le code nettement moins lisible que de suivre le bon vieux duo .h/.cpp:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    // .h
    extern foldl_fn foldl;
    extern std::plus<> plus;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    // .cpp
    foldl_fn foldl;
    std::plus<> plus;
    n'est-ce pas plus simple?

  13. #13
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    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 : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    La gestion d'une variable externe et d'une constante statique de classe n'est pas la même, l'emplacement mémoire diffère, il me semble (.bss/.rodata). Il y a probablement d'autres subtilités. Dans l'utilisation, il n'y aurait pas de différence et l'ODR serait respecté.

    Et oui, cela permet de s'affranchir du .cpp.

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

Discussions similaires

  1. Fonction template recursive, avec imbrication de types
    Par YéTeeh dans le forum Langage
    Réponses: 5
    Dernier message: 28/04/2006, 17h02
  2. Pointeur sur une fonction template
    Par Progs dans le forum Langage
    Réponses: 2
    Dernier message: 15/02/2006, 20h25
  3. Fonction template virtuelle... comment l'éviter ?
    Par :Bronsky: dans le forum Langage
    Réponses: 12
    Dernier message: 07/06/2005, 14h21
  4. fonctions template
    Par romeo9423 dans le forum Langage
    Réponses: 12
    Dernier message: 22/01/2005, 16h13
  5. Fonctions template+friend sous VC7
    Par patapetz dans le forum MFC
    Réponses: 12
    Dernier message: 24/09/2004, 11h16

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