Je m'interroge simplement sur les raisons qui ont présidées à la
création des "function objects" et sur la bonne manière de les
utiliser. Etait-t'il nécessaire d'inventer cette "nouvelle tentacule"
? Y-a t'il des exemples dans lesquelles il n'est pas possible de passer
outre par un design ? Par exemple, si je ne m'abuse, l'emploi des
objets fonction dans les algorithmes sort() de la STL revient ni plus
ni moins à l'utilisation d'un pattern de type strategie.
Je m'interroge simplement sur les raisons qui ont présidées à la
création des "function objects" et sur la bonne manière de les
utiliser. Etait-t'il nécessaire d'inventer cette "nouvelle tentacule"
? Y-a t'il des exemples dans lesquelles il n'est pas possible de passer
outre par un design ? Par exemple, si je ne m'abuse, l'emploi des
objets fonction dans les algorithmes sort() de la STL revient ni plus
ni moins à l'utilisation d'un pattern de type strategie.
Je m'interroge simplement sur les raisons qui ont présidées à la
création des "function objects" et sur la bonne manière de les
utiliser. Etait-t'il nécessaire d'inventer cette "nouvelle tentacule"
? Y-a t'il des exemples dans lesquelles il n'est pas possible de passer
outre par un design ? Par exemple, si je ne m'abuse, l'emploi des
objets fonction dans les algorithmes sort() de la STL revient ni plus
ni moins à l'utilisation d'un pattern de type strategie.
Je m'interroge simplement sur les raisons qui ont présidées à
la création des "function objects" et sur la bonne manière de
les utiliser. Etait-t'il nécessaire d'inventer cette "nouvelle
tentacule" ? Y-a t'il des exemples dans lesquelles il n'est
pas possible de passer outre par un design ? Par exemple, si
je ne m'abuse, l'emploi des objets fonction dans les
algorithmes sort() de la STL revient ni plus ni moins à
l'utilisation d'un pattern de type strategie.
Je m'interroge simplement sur les raisons qui ont présidées à
la création des "function objects" et sur la bonne manière de
les utiliser. Etait-t'il nécessaire d'inventer cette "nouvelle
tentacule" ? Y-a t'il des exemples dans lesquelles il n'est
pas possible de passer outre par un design ? Par exemple, si
je ne m'abuse, l'emploi des objets fonction dans les
algorithmes sort() de la STL revient ni plus ni moins à
l'utilisation d'un pattern de type strategie.
Je m'interroge simplement sur les raisons qui ont présidées à
la création des "function objects" et sur la bonne manière de
les utiliser. Etait-t'il nécessaire d'inventer cette "nouvelle
tentacule" ? Y-a t'il des exemples dans lesquelles il n'est
pas possible de passer outre par un design ? Par exemple, si
je ne m'abuse, l'emploi des objets fonction dans les
algorithmes sort() de la STL revient ni plus ni moins à
l'utilisation d'un pattern de type strategie.
(prédicats de type vérifiés par le compilateur).
(prédicats de type vérifiés par le compilateur).
(prédicats de type vérifiés par le compilateur).
la légereté, ça à la limite je comprends. L'inlining complet,
là déjà, je coinces... Je crts savoir ce qu'est l'inlining
mais je ne vois pas en quoi les objets fonction le permettent
plus qu'un autre modèle à base de fonction ou de classes
dérivées.
En ce qui concerne le cas de figure énnoncé par Kanze avec la
fonction isNotEspace, je pars avec un gros handicap : je ne
suis pas sensibilisé au problème des locale, j'y ai jeté un
oeil rapide mais ça a l'air tout de meme un peu trop gros pour
une compréhension en 5 minutes :) Bref, std::use_facet, Ctype
et Cie, ça ne me parle pas trop, du coup je vois pas le
problème ! ;)
L'exemple concret, bien qu'un peu complexe par contre me
permet déjà mieux de tutoyer des éléments de réponse. moi,
naivement j'aurai passé le pattern à trouver en argument :
template< bool testFor,
typename charT = char >
class Is
{
public:
typedef std::ctype< charT >
CType ;
explicit Is( std::ctype_base::mask pattern,
std::locale const& l = std::locale() )
: myCType( &std::use_locale< CType >( l ) ),
mask(pattern)
{
}
bool operator()( charT ch ) const
{
return myCType->is( mask, ch ) == testFor ;
}
private:
CType const* myCType ;
std::ctype_base::mask mask;
} ;
}
...certainement par "peur" de voir le compilo possiblement générer
autant de classes que de caractères unicodes ;)
Et ce petit exemple de me faire me remarquer qu'à partir du
moment où l'objet fonction trimbale des données ça devient pas
trop possible d'inliner, non ? C'est ça le truc ? inline ssi
pas de donnée dans l'objet ?
Il y a aussi l'histoire du gain en sécurité qui reste obscure
pour moi :(prédicats de type vérifiés par le compilateur).
Je remarques que j'utilise peut etre les objets fonctionnels
un peu à contre emploi ou de manière batarde à la "modèle
stratégie" (dérivation d'une classe abstraite pure template
<class O>Neighborhood avec "bool operator=(Oconst&,Oconst&)
=0" puis dérivation en NeighborhoodBySides,
NeighborhoodByVertex,... etc) ...il faut que je relise mon
code pour approfondir la question et faire un choix, parce
qu'à priori on dirait bien que je ne fais rien d'autre ici
qu'un modèle dans lequel la fonction à fournir s'appelle
operator= , mais elle pourrait tout aussi bien s'appeller foo
!
la légereté, ça à la limite je comprends. L'inlining complet,
là déjà, je coinces... Je crts savoir ce qu'est l'inlining
mais je ne vois pas en quoi les objets fonction le permettent
plus qu'un autre modèle à base de fonction ou de classes
dérivées.
En ce qui concerne le cas de figure énnoncé par Kanze avec la
fonction isNotEspace, je pars avec un gros handicap : je ne
suis pas sensibilisé au problème des locale, j'y ai jeté un
oeil rapide mais ça a l'air tout de meme un peu trop gros pour
une compréhension en 5 minutes :) Bref, std::use_facet, Ctype
et Cie, ça ne me parle pas trop, du coup je vois pas le
problème ! ;)
L'exemple concret, bien qu'un peu complexe par contre me
permet déjà mieux de tutoyer des éléments de réponse. moi,
naivement j'aurai passé le pattern à trouver en argument :
template< bool testFor,
typename charT = char >
class Is
{
public:
typedef std::ctype< charT >
CType ;
explicit Is( std::ctype_base::mask pattern,
std::locale const& l = std::locale() )
: myCType( &std::use_locale< CType >( l ) ),
mask(pattern)
{
}
bool operator()( charT ch ) const
{
return myCType->is( mask, ch ) == testFor ;
}
private:
CType const* myCType ;
std::ctype_base::mask mask;
} ;
}
...certainement par "peur" de voir le compilo possiblement générer
autant de classes que de caractères unicodes ;)
Et ce petit exemple de me faire me remarquer qu'à partir du
moment où l'objet fonction trimbale des données ça devient pas
trop possible d'inliner, non ? C'est ça le truc ? inline ssi
pas de donnée dans l'objet ?
Il y a aussi l'histoire du gain en sécurité qui reste obscure
pour moi :
(prédicats de type vérifiés par le compilateur).
Je remarques que j'utilise peut etre les objets fonctionnels
un peu à contre emploi ou de manière batarde à la "modèle
stratégie" (dérivation d'une classe abstraite pure template
<class O>Neighborhood avec "bool operator=(Oconst&,Oconst&)
=0" puis dérivation en NeighborhoodBySides,
NeighborhoodByVertex,... etc) ...il faut que je relise mon
code pour approfondir la question et faire un choix, parce
qu'à priori on dirait bien que je ne fais rien d'autre ici
qu'un modèle dans lequel la fonction à fournir s'appelle
operator= , mais elle pourrait tout aussi bien s'appeller foo
!
la légereté, ça à la limite je comprends. L'inlining complet,
là déjà, je coinces... Je crts savoir ce qu'est l'inlining
mais je ne vois pas en quoi les objets fonction le permettent
plus qu'un autre modèle à base de fonction ou de classes
dérivées.
En ce qui concerne le cas de figure énnoncé par Kanze avec la
fonction isNotEspace, je pars avec un gros handicap : je ne
suis pas sensibilisé au problème des locale, j'y ai jeté un
oeil rapide mais ça a l'air tout de meme un peu trop gros pour
une compréhension en 5 minutes :) Bref, std::use_facet, Ctype
et Cie, ça ne me parle pas trop, du coup je vois pas le
problème ! ;)
L'exemple concret, bien qu'un peu complexe par contre me
permet déjà mieux de tutoyer des éléments de réponse. moi,
naivement j'aurai passé le pattern à trouver en argument :
template< bool testFor,
typename charT = char >
class Is
{
public:
typedef std::ctype< charT >
CType ;
explicit Is( std::ctype_base::mask pattern,
std::locale const& l = std::locale() )
: myCType( &std::use_locale< CType >( l ) ),
mask(pattern)
{
}
bool operator()( charT ch ) const
{
return myCType->is( mask, ch ) == testFor ;
}
private:
CType const* myCType ;
std::ctype_base::mask mask;
} ;
}
...certainement par "peur" de voir le compilo possiblement générer
autant de classes que de caractères unicodes ;)
Et ce petit exemple de me faire me remarquer qu'à partir du
moment où l'objet fonction trimbale des données ça devient pas
trop possible d'inliner, non ? C'est ça le truc ? inline ssi
pas de donnée dans l'objet ?
Il y a aussi l'histoire du gain en sécurité qui reste obscure
pour moi :(prédicats de type vérifiés par le compilateur).
Je remarques que j'utilise peut etre les objets fonctionnels
un peu à contre emploi ou de manière batarde à la "modèle
stratégie" (dérivation d'une classe abstraite pure template
<class O>Neighborhood avec "bool operator=(Oconst&,Oconst&)
=0" puis dérivation en NeighborhoodBySides,
NeighborhoodByVertex,... etc) ...il faut que je relise mon
code pour approfondir la question et faire un choix, parce
qu'à priori on dirait bien que je ne fais rien d'autre ici
qu'un modèle dans lequel la fonction à fournir s'appelle
operator= , mais elle pourrait tout aussi bien s'appeller foo
!
Ici, le compilateur voit bien quelle fonction serait appelée, et
peut la générer en ligne.
Si je comprends bien , c'est le fait de passer d'une double
à quoi sert un paramètre qui ne contient pas de
données, et dont la fonction qu'on appelle n'est pas virtuelle,
et donc est toujours la même ?
Certes, et c'est pourquoi j'ai toujours considéré les objets
Ici, le compilateur voit bien quelle fonction serait appelée, et
peut la générer en ligne.
Si je comprends bien , c'est le fait de passer d'une double
à quoi sert un paramètre qui ne contient pas de
données, et dont la fonction qu'on appelle n'est pas virtuelle,
et donc est toujours la même ?
Certes, et c'est pourquoi j'ai toujours considéré les objets
Ici, le compilateur voit bien quelle fonction serait appelée, et
peut la générer en ligne.
Si je comprends bien , c'est le fait de passer d'une double
à quoi sert un paramètre qui ne contient pas de
données, et dont la fonction qu'on appelle n'est pas virtuelle,
et donc est toujours la même ?
Certes, et c'est pourquoi j'ai toujours considéré les objets
Une question subsiste, pourquoi avoir redéfini () ? Est-ce que la
définition d'une fonction quelconque n'aurait pas suffi ?
Une question subsiste, pourquoi avoir redéfini () ? Est-ce que la
définition d'une fonction quelconque n'aurait pas suffi ?
Une question subsiste, pourquoi avoir redéfini () ? Est-ce que la
définition d'une fonction quelconque n'aurait pas suffi ?
std::cout << "apply(f1) = " << apply(f1) << "n";
O_o'
std::cout << "apply(f1) = " << apply(f1) << "n";
O_o'
std::cout << "apply(f1) = " << apply(f1) << "n";
O_o'
std::cout << "apply(f1) = " << apply(f1) << "n";
O_o'
Euh... On est pas obligé d'instancier les templates ?
apply<int (*)()>(f1) ????
bonjour,
std::cout << "apply(f1) = " << apply(f1) << "n";
O_o'
Euh... On est pas obligé d'instancier les templates ?
apply<int (*)()>(f1) ????
bonjour,
std::cout << "apply(f1) = " << apply(f1) << "n";
O_o'
Euh... On est pas obligé d'instancier les templates ?
apply<int (*)()>(f1) ????
bonjour,
std::cout << "apply(f1) = " << apply(f1) << "n";
Euh... On est pas obligé d'instancier les templates ?
apply<int (*)()>(f1) ????
std::cout << "apply(f1) = " << apply(f1) << "n";
Euh... On est pas obligé d'instancier les templates ?
apply<int (*)()>(f1) ????
std::cout << "apply(f1) = " << apply(f1) << "n";
Euh... On est pas obligé d'instancier les templates ?
apply<int (*)()>(f1) ????
Ici, le compilateur voit bien quelle fonction serait appelée, et
peut la générer en ligne.
Si je comprends bien , c'est le fait de passer d'une double
indirrection à une simple indirrection "qui sauve" ?
void h(){...};
f(void (*pf)()){...;pf();...} // ici on ne sait pas encore ce qui va
etre appellé
f(h); // on ne le sait que là
alors que:
struct F { void operator()(){...}; };
f(F pf){...;pf();...} // on le sait déjà et le compilo peut déjà
inliner
à quoi sert un paramètre qui ne contient pas de
données, et dont la fonction qu'on appelle n'est pas virtuelle,
et donc est toujours la même ?
Certes, et c'est pourquoi j'ai toujours considéré les objets
fonctions avec une partie données initialisée par un constructeur :
class IsInfTo{
private :
int compare;
public :
IsInfTo(int c):compare(c){}
bool operator(int to_be_compare){return(to_be_compare<compare);}
}
Avec le int possiblement transformé en T, paramètre template (ce qui
au passage me laisse toujours un drole d'arrière gout dans la mesure
où ma classe dépend alors de l'existence d'un operateur< dans la
classe T... Mais c'est un autre thread que j'ouvrirai certainement un
jour.).
Avec tes commentaires j'ai enfin compris pas mal de choses :
La "variable" compare peut etre passée en parametre template ou en
parametre de constructeur sans modifier le comportement du compilo en
inline. En effet, ça n'impacte pas vraiment sur le code, cela le
modifie simplement dans la modification d'une constante en une adresse
mémoire de variable. On retombe donc juste sur deux problèmes :
a. si l'operateur () de l'objet fonction est effectivement inliné,
la différrence entre les deux approches consiste dans l'ajout d'un
déréférencement
b. si l'operateur () de l'objet fonction n'est pas inliné, on
remplace un appel à des fonctions différentes par un
déréférencement de variable lors des appels. C'est à dire un code
plus long (mais moins que dans le cas b., contre un code plus court
mais plus lent).
Une question subsiste, pourquoi avoir redéfini () ? Est-ce que la
définition d'une fonction quelconque n'aurait pas suffi ?
Pour
reprendre l'exemple du début :
struct F { F(); void toto(){...}; };
f(F pf){...;pf.toto();...} // on sait déjà quelle fonction toto,
d'où inline possible, non ?
J'ai bien un élément de réponse à ma question : par soucis de
clarté du code :
IsInfTo<int,7> isIntInfTo7;
isIntInfTo7(9);
est peut etre plus clair que
IsInfTo<int,7> is7;
is7.biggerThan(9);
Mais je ne suis pas super convaincu... :)
Ici, le compilateur voit bien quelle fonction serait appelée, et
peut la générer en ligne.
Si je comprends bien , c'est le fait de passer d'une double
indirrection à une simple indirrection "qui sauve" ?
void h(){...};
f(void (*pf)()){...;pf();...} // ici on ne sait pas encore ce qui va
etre appellé
f(h); // on ne le sait que là
alors que:
struct F { void operator()(){...}; };
f(F pf){...;pf();...} // on le sait déjà et le compilo peut déjà
inliner
à quoi sert un paramètre qui ne contient pas de
données, et dont la fonction qu'on appelle n'est pas virtuelle,
et donc est toujours la même ?
Certes, et c'est pourquoi j'ai toujours considéré les objets
fonctions avec une partie données initialisée par un constructeur :
class IsInfTo{
private :
int compare;
public :
IsInfTo(int c):compare(c){}
bool operator(int to_be_compare){return(to_be_compare<compare);}
}
Avec le int possiblement transformé en T, paramètre template (ce qui
au passage me laisse toujours un drole d'arrière gout dans la mesure
où ma classe dépend alors de l'existence d'un operateur< dans la
classe T... Mais c'est un autre thread que j'ouvrirai certainement un
jour.).
Avec tes commentaires j'ai enfin compris pas mal de choses :
La "variable" compare peut etre passée en parametre template ou en
parametre de constructeur sans modifier le comportement du compilo en
inline. En effet, ça n'impacte pas vraiment sur le code, cela le
modifie simplement dans la modification d'une constante en une adresse
mémoire de variable. On retombe donc juste sur deux problèmes :
a. si l'operateur () de l'objet fonction est effectivement inliné,
la différrence entre les deux approches consiste dans l'ajout d'un
déréférencement
b. si l'operateur () de l'objet fonction n'est pas inliné, on
remplace un appel à des fonctions différentes par un
déréférencement de variable lors des appels. C'est à dire un code
plus long (mais moins que dans le cas b., contre un code plus court
mais plus lent).
Une question subsiste, pourquoi avoir redéfini () ? Est-ce que la
définition d'une fonction quelconque n'aurait pas suffi ?
Pour
reprendre l'exemple du début :
struct F { F(); void toto(){...}; };
f(F pf){...;pf.toto();...} // on sait déjà quelle fonction toto,
d'où inline possible, non ?
J'ai bien un élément de réponse à ma question : par soucis de
clarté du code :
IsInfTo<int,7> isIntInfTo7;
isIntInfTo7(9);
est peut etre plus clair que
IsInfTo<int,7> is7;
is7.biggerThan(9);
Mais je ne suis pas super convaincu... :)
Ici, le compilateur voit bien quelle fonction serait appelée, et
peut la générer en ligne.
Si je comprends bien , c'est le fait de passer d'une double
indirrection à une simple indirrection "qui sauve" ?
void h(){...};
f(void (*pf)()){...;pf();...} // ici on ne sait pas encore ce qui va
etre appellé
f(h); // on ne le sait que là
alors que:
struct F { void operator()(){...}; };
f(F pf){...;pf();...} // on le sait déjà et le compilo peut déjà
inliner
à quoi sert un paramètre qui ne contient pas de
données, et dont la fonction qu'on appelle n'est pas virtuelle,
et donc est toujours la même ?
Certes, et c'est pourquoi j'ai toujours considéré les objets
fonctions avec une partie données initialisée par un constructeur :
class IsInfTo{
private :
int compare;
public :
IsInfTo(int c):compare(c){}
bool operator(int to_be_compare){return(to_be_compare<compare);}
}
Avec le int possiblement transformé en T, paramètre template (ce qui
au passage me laisse toujours un drole d'arrière gout dans la mesure
où ma classe dépend alors de l'existence d'un operateur< dans la
classe T... Mais c'est un autre thread que j'ouvrirai certainement un
jour.).
Avec tes commentaires j'ai enfin compris pas mal de choses :
La "variable" compare peut etre passée en parametre template ou en
parametre de constructeur sans modifier le comportement du compilo en
inline. En effet, ça n'impacte pas vraiment sur le code, cela le
modifie simplement dans la modification d'une constante en une adresse
mémoire de variable. On retombe donc juste sur deux problèmes :
a. si l'operateur () de l'objet fonction est effectivement inliné,
la différrence entre les deux approches consiste dans l'ajout d'un
déréférencement
b. si l'operateur () de l'objet fonction n'est pas inliné, on
remplace un appel à des fonctions différentes par un
déréférencement de variable lors des appels. C'est à dire un code
plus long (mais moins que dans le cas b., contre un code plus court
mais plus lent).
Une question subsiste, pourquoi avoir redéfini () ? Est-ce que la
définition d'une fonction quelconque n'aurait pas suffi ?
Pour
reprendre l'exemple du début :
struct F { F(); void toto(){...}; };
f(F pf){...;pf.toto();...} // on sait déjà quelle fonction toto,
d'où inline possible, non ?
J'ai bien un élément de réponse à ma question : par soucis de
clarté du code :
IsInfTo<int,7> isIntInfTo7;
isIntInfTo7(9);
est peut etre plus clair que
IsInfTo<int,7> is7;
is7.biggerThan(9);
Mais je ne suis pas super convaincu... :)