|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
Bonjour à tous,
J'ai essayé de faire un template qui donne la fonction composée de deux fonction (sans utilisé de "functor"). Je suis arrivé à ça : #include <iostream> template<typename T1,typename T2,typename T3, T1 f1(T2), T2 f2(T3)> T1 compose_fct(T3 arg) { return f1(f2(arg)); }; int twice(int a) { return 2*a; } int square(int a) { return a*a; } int main() { int (*sqr_tw)(int) = compose_fct<int,int,int,square,twice>; std::cout << sqr_tw(4) << " :: " << square(twice(4)) << std::endl; std::cout << sqr_tw(5) << " :: " << square(twice(5)) << std::endl; std::cout << sqr_tw(6) << " :: " << square(twice(6)) << std::endl; return 0; } (testé avec gcc 3.4.4 et VS2005) Je trouve dommage d'être obligé de mettre ces 3 "int" dans compose_fct<int,int,int,square,twice> alors qu'on a toutes les informations nécéssaire avec le type des functions. Personne n'a une idée pour améliorer ça ? Seb |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
"plouf" <plouf79@yahoo.fr> a écrit dans le message de news: LZLRj.106$yk.36@nntpserver.swip.net... > Bonjour à tous, > > J'ai essayé de faire un template qui donne la fonction composée de deux > fonction (sans utilisé de "functor"). Je suis arrivé à ça : > > #include <iostream> > > template<typename T1,typename T2,typename T3, T1 f1(T2), T2 f2(T3)> > T1 compose_fct(T3 arg) > { > return f1(f2(arg)); > }; > > int twice(int a) > { > return 2*a; > } > > int square(int a) > { > return a*a; > } > > int main() > { > int (*sqr_tw)(int) = compose_fct<int,int,int,square,twice>; > > std::cout << sqr_tw(4) << " :: " << square(twice(4)) << std::endl; > std::cout << sqr_tw(5) << " :: " << square(twice(5)) << std::endl; > std::cout << sqr_tw(6) << " :: " << square(twice(6)) << std::endl; > > return 0; > } > > (testé avec gcc 3.4.4 et VS2005) > > Je trouve dommage d'être obligé de mettre ces 3 "int" dans > compose_fct<int,int,int,square,twice> alors qu'on a toutes les > informations nécéssaire avec le type des functions. > > Personne n'a une idée pour améliorer ça ? Très intéressant. Bravo ! |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
plouf a écrit :
> Bonjour à tous, > > J'ai essayé de faire un template qui donne la fonction composée de > deux fonction (sans utilisé de "functor"). Je suis arrivé à ça : [snip] > int (*sqr_tw)(int) = compose_fct<int,int,int,square,twice>; [snip] > Je trouve dommage d'être obligé de mettre ces 3 "int" dans > compose_fct<int,int,int,square,twice> alors qu'on a toutes les > informations nécéssaire avec le type des functions. > > Personne n'a une idée pour améliorer ça ? Pas vraiment. Spécialiser sur R(*T)(P) est spécialiser sur une valeur; pour avoir une valeur, on est bien obligé d'avoir un type. Imagine que square ait plusieurs spécialisations: double square(double) float square(float) int square(int) Se contenter de: compose_fct<square,twice> ne permettrait pas de choisir la bonne valeur. -- Michael |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
Michael DOUBEZ a écrit :
.... > > Pas vraiment. Spécialiser sur R(*T)(P) est spécialiser sur une valeur; > pour avoir une valeur, on est bien obligé d'avoir un type. > > Imagine que square ait plusieurs spécialisations: > double square(double) > float square(float) > int square(int) > > Se contenter de: > compose_fct<square,twice> > ne permettrait pas de choisir la bonne valeur. > Ah oui, je n'avais pas pensé à ça. C'est donc logique qu'on ne puisse pas simplifier plus. Merci. Seb |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
30 Apr 2008 14:49:46 +0200, Michael DOUBEZ :
>Imagine que square ait plusieurs spécialisations: >double square(double) >float square(float) >int square(int) Mais en l'occurence, on n'en a qu'une. Le compilateur est capable de déduire les types qui vont bien dans un tel cas. Exemple : le code ci-dessous est parfaitement valide, et cesse de l'être quand on enlève les "//". int square (int); //double square (double); template <class Resultat, class Argument> void F (Resultat (*fct) (Argument)) {} int main() { F (square); } |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
On Tue, 29 Apr 2008 22:58:53 +0200, plouf <plouf79@yahoo.fr>:
>[...] J'ai bien conscience que le code ci-dessous ne répond pas à la question, mais c'est la seule solution que j'aie trouvé pour ne jamais avoir à écrire les types dans le code client. template <typename Resultat, typename Intermediaire, typename Argument> class FoncteurCompose_t { public: typedef Resultat (*Fonction1)(Intermediaire); typedef Intermediaire (*Fonction2)(Argument); FoncteurCompose_t (Fonction1 f1_, Fonction2 f2_) : f1 (f1_), f2 (f2_) {} Resultat operator () (Argument const& a) const { return f1(f2(a)); } private: Fonction1 f1; Fonction2 f2; }; template <typename Resultat, typename Intermediaire, typename Argument> FoncteurCompose_t <Resultat, Intermediaire, Argument> FoncteurCompose (Resultat (*f1)(Intermediaire), Intermediaire (*f2)(Argument)) { return FoncteurCompose_t <Resultat, Intermediaire, Argument> (f1, f2); } int twice(int a) { return 2*a; } int square(int a) { return a*a; } #include <iostream> template <class F> void Teste (F f) { std::cout << f(4) << " :: " << square(twice(4)) << std::endl; std::cout << f(5) << " :: " << square(twice(5)) << std::endl; std::cout << f(6) << " :: " << square(twice(6)) << std::endl; } int main() { Teste (FoncteurCompose (square, twice)); } |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
On Tue, 29 Apr 2008 22:58:53 +0200, plouf <plouf79@yahoo.fr>:
>[...] J'ai bien conscience que le code ci-dessous ne répond pas à la question, mais c'est la seule solution que j'aie trouvé pour ne jamais avoir à écrire les types dans le code client. template <typename Resultat, typename Intermediaire, typename Argument> class FoncteurCompose_t { public: typedef Resultat (*Fonction1)(Intermediaire); typedef Intermediaire (*Fonction2)(Argument); FoncteurCompose_t (Fonction1 f1_, Fonction2 f2_) : f1 (f1_), f2 (f2_) {} Resultat operator () (Argument const& a) const { return f1(f2(a)); } private: Fonction1 f1; Fonction2 f2; }; template <typename Resultat, typename Intermediaire, typename Argument> FoncteurCompose_t <Resultat, Intermediaire, Argument> FoncteurCompose (Resultat (*f1)(Intermediaire), Intermediaire (*f2)(Argument)) { return FoncteurCompose_t <Resultat, Intermediaire, Argument> (f1, f2); } int twice(int a) { return 2*a; } int square(int a) { return a*a; } #include <iostream> template <class F> void Teste (F f) { std::cout << f(4) << " :: " << square(twice(4)) << std::endl; std::cout << f(5) << " :: " << square(twice(5)) << std::endl; std::cout << f(6) << " :: " << square(twice(6)) << std::endl; } int main() { Teste (FoncteurCompose (square, twice)); } |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
On Tue, 29 Apr 2008 22:58:53 +0200, plouf <plouf79@yahoo.fr>:
> Je trouve dommage d'être obligé de mettre ces 3 "int" dans >compose_fct<int,int,int,square,twice> alors qu'on a toutes les informations >nécéssaire avec le type des functions. J'ai trouvé le moyen d'éviter de spécifier ces trois "int"... à condition de taper les noms des fonctions deux fois ! C'est le code le plus tordu que j'aie pondu depuis un bout de temps... /* La fonction finale. Aux noms des identifiants près, il s'agit de ton code. */ template <typename Resultat, typename Intermediaire, typename Argument, Resultat f1 (Intermediaire), Intermediaire f2 (Argument)> Resultat compose_fct(Argument arg) { return f1(f2(arg)); }; /* Ici, on sépare les arguments templates "types" des arguments templates "fonctions". L'idée : puisque les deux fonctions sont des arguments templates, il faut que les trois types soient déjà définis. La seule façon de faire, c'est de mettre "GetF" dans une classe template. */ template <typename Resultat, typename Intermediaire, typename Argument> struct Composeur_t { typedef Resultat (*Reponse) (Argument); template <Resultat f1 (Intermediaire), Intermediaire f2 (Argument)> Reponse GetF() const { return compose_fct <Resultat, Intermediaire, Argument, f1, f2>; } }; /* Le compilateur est capable de déduire automatiquement les paramètres template pour une fonction, mais pas pour une classe. On crée donc une fonction "er" qui crée un objet de type Composeur_t. */ template <typename Resultat, typename Intermediaire, typename Argument> Composeur_t <Resultat, Intermediaire, Argument> Composeur (Resultat (*f1)(Intermediaire), Intermediaire (*f2)(Argument)) { return Composeur_t <Resultat, Intermediaire, Argument>(); } // Tes deux fonctions int twice(int a) { return 2*a; } int square(int a) { return a*a; } // Le code utilisateur int main() { typedef int (*fonction) (int); fonction f= Composeur(square,twice).GetF<square,twice>(); } |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
> fonction f= Composeur(square,twice).GetF<square,twice>();
Et évidemment, on peut utiliser une macro pour éviter la répétition : #define COMPOSE( f1, f2) (Composeur(f1,f2).GetF<f1,f2>()) fonction f= COMPOSE (square, twice); |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
Fabien LE LEZ a écrit :
>> fonction f= Composeur(square,twice).GetF<square,twice>(); > > Et évidemment, on peut utiliser une macro pour éviter la répétition : > > #define COMPOSE( f1, f2) (Composeur(f1,f2).GetF<f1,f2>()) > > fonction f= COMPOSE (square, twice); > J'aime bien tes deux méthodes. De mon coté je suis parti un peu sur une autre voix en remplaçant les trois "int" par le type des deux méthodes et en essayant de faire quelque chose qui permette de composer des fonction composée. Je suis arrivé à un truc assez marrant qui permet de "définir" les nouvelles fonction avec des typedef. La syntaxe et horrible et je ne pense pas que j'utiliserais ça un jour dans un vrai programme :-D #include <iostream> #include <string> #include <sstream> using std::string; template<typename F> class fct_type; template<typename Res, typename Arg> struct fct_type<Res (*)(Arg)> { typedef Res res; typedef Arg arg; }; template<typename F, typename G> struct compose { typedef typename fct_type<F>::res F_res; typedef typename fct_type<F>::arg F_arg; typedef typename fct_type<G>::res G_res; typedef typename fct_type<G>::arg G_arg; template<F_res f(F_arg), G_res g(G_arg)> struct prm { typedef F_res (*type)(G_arg); static F_res func(G_arg arg) { return f(g(arg)); } }; }; int twice(int a) { return 2*a; } int square(int a) { return a*a; } std::string toString (int i) { std::stringstream sstr; sstr << "STR(" << i << ")"; return sstr.str(); } void test(int n) { typedef int (* i_i)(int); // int -> int typedef string (* i_s)(int); // int -> string typedef compose<i_i,i_i>::prm<square,twice> sqr_tw; typedef compose<i_i,i_i>::prm<twice,twice> x4; typedef compose<i_i,i_i>::prm<square,square> sqr_sqr; typedef compose<i_i,i_i>::prm<square,sqr_tw::func> sqr_sqr_tw1; typedef compose<i_i,i_i>::prm<sqr_sqr::func,twice> sqr_sqr_tw2; // On peut utiliser i_i ou "x4::type" si on ne connait pas le type typedef compose<i_i,x4::type>::prm<x4::func,x4::func> x16; typedef compose<i_s,x16::type>::prm<toString,x16::func> x16_str; std::cout << sqr_tw::func(n) << " :: " << square(twice(n)) << std::endl; std::cout << sqr_sqr_tw1::func(n) << " :: " << square(square(twice(n))) << std::endl; std::cout << sqr_sqr_tw2::func(n) << " :: " << square(square(twice(n))) << std::endl; std::cout << x16_str::func(n) << " :: " << x4::func(x4::func(n)) << std::endl; std::cout << std::endl; } int main() { test(4); test(5); test(6); return 0; } Seb |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
On Sat, 03 May 2008 00:02:26 +0200, plouf <plouf79@yahoo.fr>:
>La syntaxe et horrible Comme tu dis. Mais le préprocesseur doit pouvoir aider, non ? |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
Fabien LE LEZ a écrit :
> 30 Apr 2008 14:49:46 +0200, Michael DOUBEZ : > >> Imagine que square ait plusieurs spécialisations: >> double square(double) >> float square(float) >> int square(int) > > Mais en l'occurence, on n'en a qu'une. Le compilateur est capable de > déduire les types qui vont bien dans un tel cas. Oui. Ce n'est pas ce que j'ai dit. J'ai dit que la spécialisation du template se faisait par valeur (valeur d'un pointeur sur fonction) et que nous ne pouvions pas avoir une valeur sans typage. J'ai eu tort de mentionner la surcharge de fonction, l'exemple n'est pas approprié. Tu as raison sur ce point. Michael |
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
On Mon, 05 May 2008 09:37:32 +0200, Michael DOUBEZ
<michael.doubez@free.fr>: >Oui. Ce n'est pas ce que j'ai dit. J'ai dit que la spécialisation du >template se faisait par valeur (valeur d'un pointeur sur fonction) et >que nous ne pouvions pas avoir une valeur sans typage. Certes, mais ça ne veut pas dire que le programmeur doit expliciter le type. >J'ai eu tort de mentionner la surcharge de fonction, l'exemple n'est pas >approprié. Sauf qu'en l'occurence, c'est effectivement le cas qui pose problème. |
|
![]() |
| Outils de la discussion | |
|
|