OPTIMISATION DES CALLBACKS C++ RÉSOLUS À LA COMPILATION.

jerrol Messages postés 11 Date d'inscription mercredi 25 décembre 2002 Statut Membre Dernière intervention 29 décembre 2004 - 27 déc. 2004 à 07:38
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009 - 2 janv. 2005 à 23:48
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/28416-optimisation-des-callbacks-c-resolus-a-la-compilation

xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
2 janv. 2005 à 23:48
J'ai qd même ajouter une fonction membre "without_throw" avec un return par défaut qui effectivement ,'a que peu de sens.

Merci encore pour ton aide.
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
2 janv. 2005 à 12:46
salut,

effectivement V1 n'apporte rien, et le pire (je me confesse), c'est que je n'en étais pas conscient !
La version 2 souffre d'un gros défaut : on ne peut pas appeler what() si l'exception ne dérive pas de std::exception. On perd donc le message d'erreur. De plus, si la classe d'exception possede d'autres informations accessibles avec d'autres méthodes que what(), c'est perdu aussi (qu'elle dérive de std::exception ou non). A abandonner donc.
Pour la version 3, ça me parait difficile de retourner qqchose de cohérent ... (il n'y a pas de return)
Finalement, je pense que ton code est bon tel qu'il est.
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
30 déc. 2004 à 23:41
Dans la V1, le try/catch n'apporte rien par rapport à l'original. La V2 peut effectivement répondre à un besoin spécifique : masquer toutes les exceptions (std ou pas).

Je proposerait égallement une V3 "bete&mechante" :
inline t_return call_wo_throw( t_argument parameter ) throw ()
{
try
{
return ( _class_instance->*_method )( parameter );
}
catch(...)
{
// rien
}
}
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
29 déc. 2004 à 22:25
effectivement, ça devient pas mal je trouve ...
Je te propose 2 solutions pour propager les exceptions :

// v1 :
inline t_return call( t_argument parameter )
{
if( _class_instance == 0 )
throw bad_instance();
if( _method == 0 )
throw bad_method();
t_return tr;
try {
tr = ( _class_instance->*_method )( parameter );
}
catch(...)
{
throw; // l'appelant se débrouille
}
return tr;
}

// v2 :
on est sur que cette méthode ne déclenchera pas d'autres exceptions.
personellement, je ne définirais pas les classes d'exceptions à l'intérieur
des classes de call_back. Par contre, je les emballerai dans le meme
espace de nommage.
cette solution amene naturellemnt à faire hériter bad_instance, bad_method, bad_call_unexpected et bad_call, de bad_callback (par exemple), lui meme héritant de std::exception (comme c'est le cas).

inline t_return call( t_argument parameter ) throw (bad_instance, bad_method, bad_call, bad_call_unexpected)
{
if( _class_instance == 0 )
throw bad_instance();
if( _method == 0 )
throw bad_method();
t_return tr;
try {
tr = ( _class_instance->*_method )( parameter );
}
catch(const std::exception& e) // eventuellement !
{
throw bad_call(e.what());
}
catch(...)
{
throw bad_call_unexpected();
}
return tr;
}

Je n'ai pas de préférences :/, qu'en penses-tu ?
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
29 déc. 2004 à 12:50
Bonjour,

L'ecriture des classes callback reste perfectible. Mais cela prend une tournure intéressante ! Merci pour tes conseils. ^_^

Cordialement,
X.
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
28 déc. 2004 à 20:35
Salut,

pour les exceptions, je songe à une autre conception, faire 2 méthodes dans les t_callback*, une qui n'utilise pas les exceptions (operator()(...)), l'autre qui déclencherai des exceptions, et qui propagerai les exceptions pouvant intervenir dans la fonction appelée. C'est juste une idée ...

"A charge du developpeur soit de completer le throw par une exception standard ou de son cru"
Pourquoi pas une classe d' exception "de son cru" dérivant des exceptions standard ?

"L'appelle d'une fonction (par son pointeur) compte pour 90% dans le temps d'execution du test de callback dynamique."
Je pense qu'un bon optimiseur doit pouvoir éliminer ce code d'erreur, tout est facilement prévisible à la compilation ... Par contre, je ne suis pas sur de la résolution inline de la fonction appelée par l'intermédiaire d'un pointeur de fonction.

Avec ton main, il est intéressant de constater la différence de rapidité d'exécution entre la version const et la version non const (si on compile sans optimisations).

Sinon, ça fait plaisir de voir un programmeur sous w$ évoluant du coté clair :)

@+
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
28 déc. 2004 à 19:53
Au passage, l'execution de ce test (throw) influe tres peu sur le resultat en terme de performance. C'est pourtant du code en plus qui ne se retrouve pas dans les autres callbacks. L'appelle d'une fonction (par son pointeur) compte pour 90% dans le temps d'execution du test de callback dynamique.
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
28 déc. 2004 à 19:48
Remarque est tout a fait justifiée (encore une fois). A charge du developpeur soit de completer le throw par une exception standard ou de son cru, soit tout simplement de la supprimer (ou la traiter différemment)!

Dans l'état, le throw appelle terminate(). C'est un tout petit peu plus propre qu'un segfault (et le docteur watson qui s'en suit). L'idée y est... on peut faire mieux.
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
28 déc. 2004 à 18:56
"Toutes mes excuses pour cet oubli impardonnable !"
Vous etes pardonné mon cher mais que ça ne se renouvelle pas :)

Cette ligne me parait bizarre ...
redéclenchement de quelle exception ?
if( _class_instance 0 && _method 0 ) throw;
il vaudrait mieux déclencher une exception, voire séparer les tests et en déclencher une différente suivant le cas. Ou bien enlever la méthode set et oter les parametres optionels dans le constructeur ? ça restreint un peu son utilisation d'accord ...
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
28 déc. 2004 à 14:01
Qualitativement, la différence de performance entre un appel direct et un appel par callback statique est négligeable.

Inversement entre un callback dynamique et un callback statique, la perte de temps semble considérable.

A méditer !
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
27 déc. 2004 à 23:13
3 const ajoutés. Toutes mes excuses pour cet oubli impardonnable !
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
27 déc. 2004 à 22:39
const ici aussi :
t_callback_const( const t_class& c ) : _callback_class( c ) {}
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
27 déc. 2004 à 22:37
mouais, il manque 2 const :

template<
typename t_class,
typename t_return,
typename t_argument,
t_return ( t_class::*fm )( t_argument ) const
>
struct t_callback_const
{
t_callback_const( t_class& c ) : _callback_class( c ) {}
inline t_return operator()( t_argument arg )const
{
return (_callback_class.*fm)( arg );
}
private:
const t_class& _callback_class;
};

pour les exceptions, tu veux pouvoir gérer une méthode genre :
void A::f(void)throw(std::bad_alloc) ? mis à part un catch(...) throw, je ne vois pas comment faire mieux.

"Je suis un peu étonné de ta dernière remarque. Peux-tu préciser ? Ca m'intéresse."
Désolé, rien d'intéressant, juste pour dire que j'ai déjà vu d'horribles programme s(inspiré des pointeurs de fonctions en C) qui utilisaient abusivement les pointeurs de fonctions membres, en détriment d'une conception objet cohérente. De la à la violer ? Je suis en train d'y réfléchir ...
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
27 déc. 2004 à 18:29
Dans mon exemple, les fonctions membres devraient effectivement être const. J'ai porté peu d'attention à leur écriture.

Pour les questions/observations, j'attendais aussi les exceptions... cela dit, je vais prendre en compte cette remarque et proposer le support des fonctions membres conts. A priori, cela oblige à définir deux classes distinctes "callback" très proches l'unes de l'autres. Je vais y réfléchir !

Je suis un peu étonné de ta dernière remarque. Peux-tu préciser ? Ca m'intéresse.

Cordialement,
X.
plus_plus_fab Messages postés 232 Date d'inscription vendredi 9 janvier 2004 Statut Membre Dernière intervention 8 janvier 2005
27 déc. 2004 à 18:15
salut,

Tu attends surement qu'on te pose la question : et la "constness" ?
alors, je te la pose :)

Mise à part que les fonctions membres des exemples doivent etre déclarées const, aucune remarque sur le code, j'aime bien.
Mais je considère les procédés de ce genre comme des hacks à n'utiliser que dans des cas bien spécifiques.
xterminhate Messages postés 371 Date d'inscription dimanche 4 janvier 2004 Statut Membre Dernière intervention 23 septembre 2009
27 déc. 2004 à 08:56
Ce code compile et s'execute avec succes avec les compilateurs suivans : MinGW, Cygwin, et MS .Net 2003 (VC7).

A moins de vouloir à tout prix utiliser les MFC dans tes programmes, tu devrais changer de compilateur parce qu'il ne sait pas faire de C++ correctement (en particulier les écritures à base de template).

Je ne peux donc rien faire pour tes erreurs, désolé.

Cordialement,
X.
jerrol Messages postés 11 Date d'inscription mercredi 25 décembre 2002 Statut Membre Dernière intervention 29 décembre 2004
27 déc. 2004 à 07:38
Salut !

J'ai compilé avec Visual C++6, et j'ai les erreurs suivantes :

E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(17) : error C2973: 't_callback' : invalid template argument 'fm'
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(25) : see declaration of 't_callback'
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(88) : see reference to class template instantiation 't_callback<struct A,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,
int const &,&public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall A::fa(int const &)>' being compiled
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(103) : see reference to class template instantiation 'X<struct A,&public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
__thiscall A::fa(int const &)>' being compiled
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(17) : error C2973: 't_callback' : invalid template argument 'fm'
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(25) : see declaration of 't_callback'
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(88) : see reference to class template instantiation 't_callback<struct B,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,
int const &,&public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall B::fb(int const &)>' being compiled
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(104) : see reference to class template instantiation 'X<struct B,&public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
__thiscall B::fb(int const &)>' being compiled
E:\Program Files\Microsoft Visual Studio\MyProjects\testconsole\testconsole.cpp(107) : warning C4508: 'main' : function should return a value; 'void' return type assumed
Error executing cl.exe.

testconsole.exe - 2 error(s), 1 warning(s)

Faut-il configurer le projet d'une façon spécifique ?