Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

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

12 réponses
Avatar
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

10 réponses

1 2
Avatar
Ael Rowen Terence
"plouf" a écrit dans le message de news:
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 !

Avatar
Michael DOUBEZ
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

Avatar
plouf
...

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

Avatar
Fabien LE LEZ
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);
}

Avatar
Fabien LE LEZ
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));
}

Avatar
Fabien LE LEZ
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));
}

Avatar
Fabien LE LEZ
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>();
}

Avatar
Fabien LE LEZ
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);

Avatar
plouf
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


Avatar
Fabien LE LEZ
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 ?

1 2