Soyez le premier à donner votre avis sur cette source.
Snippet vu 7 369 fois - Téléchargée 36 fois
////////////////////////////////////////////////////////////////////// // Functor callback : ////////////////////////////////////////////////////////////////////// #include<exception> using std::exception; // Configuration : // - du type d'objet appelé, // - du type de la valeur de retour de la fonction membre appelée, // - du type de l'argument de la fonction membre appelée, // - du pointeur de fonction membre. // Callback statique vers une fonction membre non const : template< typename t_class, typename t_return, typename t_argument, t_return ( t_class::*fm )( t_argument ) > struct t_callback { t_callback( t_class& c ) : _callback_class( c ) {} inline t_return operator()( t_argument arg ) { return (_callback_class.*fm)( arg ); } private: t_class& _callback_class; }; // Callback statique vers une fonction membre 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( 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; }; // Callback "dynamique" vers une fonction membre non const : template < typename t_class, typename t_return, typename t_argument > struct t_callback_dyn { // exception struct bad_instance : exception { const char* what() const throw() { return "bad instance"; } }; // exception struct bad_method : exception { const char* what() const throw() { return "bad method"; } }; // type de fonction membre typedef t_return ( t_class::*t_method )( t_argument ); // constructeur t_callback_dyn( t_class* class_instance = 0, t_method method = 0 ) : _class_instance( class_instance ), _method( method ) { } void set( t_class* class_instance, t_method method ) { _class_instance = class_instance; _method = method; } inline t_return operator()( t_argument parameter ) { return ( _class_instance->*_method )( parameter ); } inline t_return call( t_argument parameter ) { if( _class_instance == 0 ) throw bad_instance(); if( _method == 0 ) throw bad_method(); return ( _class_instance->*_method )( parameter ); } inline t_return call_wo_throw( t_argument parameter ) throw() { try { if( _class_instance != 0 && _method != 0 ) return ( _class_instance->*_method )( parameter ); else return t_return(); } catch(...) { return t_return(); } } private: t_class* _class_instance; t_method _method; }; // Callback "dynamique" vers une fonction membre const : template < typename t_class, typename t_return, typename t_argument > struct t_callback_dyn_const { // exception struct bad_instance : exception { const char* what() const throw() { return "bad instance"; } }; // exception struct bad_method : exception { const char* what() const throw() { return "bad method"; } }; // type de fonction membre typedef t_return ( t_class::*t_method )( t_argument ) const; // constructeur t_callback_dyn_const( t_class* class_instance = 0, t_method method = 0 ) : _class_instance( class_instance ), _method( method ) { } void set( t_class* class_instance, t_method method ) { _class_instance = class_instance; _method = method; } inline t_return operator()( t_argument parameter ) const { return ( _class_instance->*_method )( parameter ); } inline t_return call( t_argument parameter ) const { if( _class_instance == 0 ) throw bad_instance(); if( _method == 0 ) throw bad_method(); return ( _class_instance->*_method )( parameter ); } inline t_return call_wo_throw( t_argument parameter ) const throw() { try { if( _class_instance != 0 && _method != 0 ) return ( _class_instance->*_method )( parameter ); else return t_return(); } catch(...) { return t_return(); } } private: t_class* _class_instance; t_method _method; }; ////////////////////////////////////////////////////////////////////// // Mise en oeuvre et exemples ////////////////////////////////////////////////////////////////////// // Variables de mesure des performances const unsigned int nb_tests = 200; const unsigned int nb_iterations = 200000000; #include<vector> #include<sstream> #include<iostream> #include<algorithm> #include<functional> #include<numeric> #include<ctime> using namespace std; // Pour l'exemple, les fonctions membres appellées ont le prototype : // "int f( const int& )" ou "int f( const int& ) const" // Objet appelé n°1 pour l'exemple. struct A { A( const int& n ) : _coef( n ) {} int fa( const int& n ) { return _coef + n; } private: int _coef; }; // Objet appelé n°2 pour l'exemple. struct B { B( const int& n ) : _coef( n ) {} int fb( const int& n ) { return _coef + n; } private: int _coef; }; // Objet appelé n°3 pour l'exemple. struct C { C( const int& n ) : _coef( n ) {} int fc( const int& n ) const { return _coef + n; } private: int _coef; }; // Objet appelant sans callback (appel codé en dur) pour l'exemple. struct W { W( A& a ) : _a( a ) {} void event() { _a.fa( 888 ); } private: A& _a; }; ////////////////////////////////////////////////////////////////////// // // Si ce n'est pas A::fa mais B::fb qu'il faut appeler, // la classe W doit être recoder en conséquence. // // Pour mettre en oeuvre un callback statique, il faut preciser au // moment de la compilation (par template), le type de l'objet appelé // et passer le pointeur vers la fonction membre appelée. // A la construction de la classe intégrant un callback, il faut // passer par référence une instance de l'objet appelé. // A l'utilisation, le callback se manipule comme la fonction membre // appelée (operateur ()). // ////////////////////////////////////////////////////////////////////// // Objet appelant avec callback "statique" (fonction membre non const) template< typename t_class, int ( t_class::*fm )( const int& ) > struct X { X( t_class& c ) : callback( c ) {} void event() { callback( 888 ); } private: t_callback< t_class, int, const int&, fm > callback; }; // Objet appelant avec callback "statique" (fonction membre const) template< typename t_class, int ( t_class::*fm )( const int& ) const > struct Y { Y( t_class& c ) : callback( c ) {} void event() { callback( 888 ); } private: t_callback_const< t_class, int, const int&, fm > callback; // callback }; // Objet appelant avec callback "dynamique" (fonction membre non const) // sans test class/methode template< typename t_class > struct Z { typedef int ( t_class::*fm )( const int& ); Z( t_class& c, fm f ) : callback( &c, f ) {} void event() { callback( 888 ); } private: t_callback_dyn< t_class, int, const int& > callback; }; // Objet appelant avec callback "dynamique" (fonction membre non const) // avec test class/methode template< typename t_class > struct Zt { typedef int ( t_class::*fm )( const int& ); Zt( t_class& c, fm f ) : callback( &c, f ) {} void event() { callback.call( 888 ); } private: t_callback_dyn< t_class, int, const int& > callback; }; // Objet appelant avec callback "dynamique" (fonction membre const) // sans test class/methode template< typename t_class > struct Zc { typedef int ( t_class::*fm )( const int& ) const; Zc( t_class& c, fm f ) : callback( &c, f ) {} void event() { callback( 888 ); } private: t_callback_dyn_const< t_class, int, const int& > callback; }; // Objet appelant avec callback "dynamique" (fonction membre const) // sans test class/methode template< typename t_class > struct Ztc { typedef int ( t_class::*fm )( const int& ) const; Ztc( t_class& c, fm f ) : callback( &c, f ) {} void event() { callback.call( 888 ); } private: t_callback_dyn_const< t_class, int, const int& > callback; }; int main() { // Varaibles de mesure des performances vector<clock_t> duree_ss_callback; vector<clock_t> duree_av_callback_sta; vector<clock_t> duree_av_callback_stac; vector<clock_t> duree_av_callback_dyn; vector<clock_t> duree_av_callback_dynt; vector<clock_t> duree_av_callback_dync; vector<clock_t> duree_av_callback_dyntc; // Objets appelés : A a( 12 ); //B b( 12 ); C c( 12 ); // Boucle de test for( int repetition = nb_tests; repetition != 0; repetition-- ) { // Test de performance avec un objet appelant sans callback : { W w( a ); clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) w.event(); duree_ss_callback.push_back( clock() - debut ); } // ... pas possible d'appeler une autre fonction membre de A, ou un autre objet sans recoder... // Test de performance avec un objet appelant avec callback statique (non const): { X<A, &A::fa> xa( a ); // Objet configuré pour appeler A::fa clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) xa.event(); duree_av_callback_sta.push_back( clock() - debut ); } // Test de performance avec un objet appelant avec callback statique (non const): { Y<C, &C::fc> yc( c ); // Objet configuré pour appeler C::fc (methode const) clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) yc.event(); duree_av_callback_stac.push_back( clock() - debut ); } // Test de performance avec un objet appelant avec callback dynamique (non const) (ss test class/methode): { Z<A> za( a, &A::fa ); // Objet configuré pour appeler A::fa clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) za.event(); duree_av_callback_dyn.push_back( clock() - debut ); } // Test de performance avec un objet appelant avec callback dynamique (non const) (av test class/methode): { Zt<A> zta( a, &A::fa ); // Objet configuré pour appeler A::fa clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) zta.event(); duree_av_callback_dynt.push_back( clock() - debut ); } // Test de performance avec un objet appelant avec callback dynamique (const) (ss test class/methode): { Zc<C> zc( c, &C::fc ); // Objet configuré pour appeler A::fa clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) zc.event(); duree_av_callback_dync.push_back( clock() - debut ); } // Test de performance avec un objet appelant avec callback dynamique (const) (ac test class/methode): { Ztc<C> ztc( c, &C::fc ); // Objet configuré pour appeler A::fa clock_t debut = clock(); for( unsigned int iteration = nb_iterations; iteration !=0; iteration-- ) ztc.event(); duree_av_callback_dyntc.push_back( clock() - debut ); } } // affichage des performances cout << "Temps d'execution sans callback : " << accumulate( duree_ss_callback.begin(), duree_ss_callback.end(), 0 ) << "ms" << endl; cout << "Temps d'execution avec callback statique (non const) : " << accumulate( duree_av_callback_sta.begin(), duree_av_callback_sta.end(), 0 ) << "ms" << endl; cout << "Temps d'execution avec callback statique (const) : " << accumulate( duree_av_callback_stac.begin(), duree_av_callback_stac.end(), 0 ) << "ms" << endl; cout << "Temps d'execution avec callback dynamique (non const)(ss test) : " << accumulate( duree_av_callback_dyn.begin(), duree_av_callback_dyn.end(), 0 ) << "ms" << endl; cout << "Temps d'execution avec callback dynamique (non const)(av test) : " << accumulate( duree_av_callback_dynt.begin(), duree_av_callback_dynt.end(), 0 ) << "ms" << endl; cout << "Temps d'execution avec callback dynamique (const)(ss test) : " << accumulate( duree_av_callback_dync.begin(), duree_av_callback_dync.end(), 0 ) << "ms" << endl; cout << "Temps d'execution avec callback dynamique (const)(av test) : " << accumulate( duree_av_callback_dyntc.begin(), duree_av_callback_dyntc.end(), 0 ) << "ms" << endl; }
27 déc. 2004 à 07:38
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 ?
27 déc. 2004 à 08:56
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.
27 déc. 2004 à 18:15
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.
27 déc. 2004 à 18:29
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.
27 déc. 2004 à 22:37
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 ...
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.