Composition de function avec un "Non-Type Template Parameters"

Le
plouf
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
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Ael Rowen Terence
Le #6435511
"plouf" LZLRj.106$
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 !

Michael DOUBEZ
Le #6437801
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

plouf
Le #6440821
...

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

Fabien LE LEZ
Le #6442351
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);
}

Fabien LE LEZ
Le #6442341
On Tue, 29 Apr 2008 22:58:53 +0200, plouf
[...]


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));
}

Fabien LE LEZ
Le #6442331
On Tue, 29 Apr 2008 22:58:53 +0200, plouf
[...]


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));
}

Fabien LE LEZ
Le #6442321
On Tue, 29 Apr 2008 22:58:53 +0200, plouf
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 "helper" 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>();
}

Fabien LE LEZ
Le #6486241
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);

plouf
Le #6540611
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


Fabien LE LEZ
Le #6551961
On Sat, 03 May 2008 00:02:26 +0200, plouf
La syntaxe et horrible


Comme tu dis. Mais le préprocesseur doit pouvoir aider, non ?

Publicité
Poster une réponse
Anonyme