begin process at 2008 08 08 21:46:54
1 223 607 membres
365 nouveaux aujourd'hui
14 230 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

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


Information sur la source

Catégorie :Astuces Niveau : Initié Date de création : 26/12/2004 Date de mise à jour : 31/12/2004 09:09:05 Vu : 4 187

Note :
Aucune note

Commentaire sur cette source (17)
Ajouter un commentaire et/ou une note

Description

Voici une classe de base pour réaliser simplement des callbacks "statiques" en C++. J'entends par "statiques", des callbacks pouvant être résolus au moment de la compilation.

La configuration de l'objet et de la fonction membre appelés est réalisée par template. Ainsi la mise en oeuvre du callback n'induit aucune surcharge à l'execution (à condition d'utiliser un compilateur/optimiseur performant).

Source

  • //////////////////////////////////////////////////////////////////////
  • // 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;
  • }
//////////////////////////////////////////////////////////////////////
// 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;
}


Conclusion

Limitation :
- Ne compile pas sous VC6 (testé avec MinGW, Cygwin et Visual.Net-7.1).

Observation :
- Excellent niveau de performance avec Visual.Net-7.1 pour les callbacks statiques (inlining poussé),
- Optimisation peu convaincante avec GCC/win32 (Cygwin et MinGW).

A faire :
- Améliorer la génération et la propagation des exceptions dans toutes les classes callback.

Toutes remarques sont les bienvenues ! ;-)
26 décembre 2004 22:37:09 :
Mise en forme.
26 décembre 2004 23:03:07 :
Modification du titre.
27 décembre 2004 21:20:55 :
Ajout d'une seconde classe callback capable d'appeler une fonction membre const.
27 décembre 2004 21:21:47 :
Mise en forme.
27 décembre 2004 21:23:14 :
Mise en forme (oui, je persiste!)
27 décembre 2004 23:15:11 :
Correction de t_callback_const.
28 décembre 2004 13:58:03 :
Code (mesure des performances) & Screenshot (resultat avec compilateur .Net 2003 - VC7).
29 décembre 2004 12:39:24 :
Ajout d'une classe callback dynamique adaptée aux fonctions membres const. Ajout d'une fonction membre dans la classe callback dynamique sans test de validité du callback. Ajout de plusieurs mesures de performances (callback dynamique av/ss const, av/ss test validité). Amélioration du mécanisme d'exception de la classe callback dynamique. Mise à jour du screenchot des resultats du test (VC7.1 / -O2). Mise en forme.
29 décembre 2004 12:46:07 :
Correction d'erreur.
30 décembre 2004 23:47:56 :
Ajout d'une fonction membre dans les classes callback dynamiques en throw().
31 décembre 2004 09:09:05 :
Correction d'erreur.
  • signaler à un administrateur
    Commentaire de jerrol le 27/12/2004 07:38:46

    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 ?

  • signaler à un administrateur
    Commentaire de xterminhate le 27/12/2004 08:56:20

    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.

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 27/12/2004 18:15:54

    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.

  • signaler à un administrateur
    Commentaire de xterminhate le 27/12/2004 18:29:57

    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.

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 27/12/2004 22:37:40

    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 ...

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 27/12/2004 22:39:51

    const ici aussi :
    t_callback_const( const t_class& c ) : _callback_class( c )   {}

  • signaler à un administrateur
    Commentaire de xterminhate le 27/12/2004 23:13:13

    3 const ajoutés. Toutes mes excuses pour cet oubli impardonnable !

  • signaler à un administrateur
    Commentaire de xterminhate le 28/12/2004 14:01:02

    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 !

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 28/12/2004 18:56:05

    "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 ...

  • signaler à un administrateur
    Commentaire de xterminhate le 28/12/2004 19:48:23

    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.

  • signaler à un administrateur
    Commentaire de xterminhate le 28/12/2004 19:53: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.

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 28/12/2004 20:35:28

    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 :)

    @+

  • signaler à un administrateur
    Commentaire de xterminhate le 29/12/2004 12:50:45

    Bonjour,

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

    Cordialement,
    X.

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 29/12/2004 22:25:29

    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 ?

  • signaler à un administrateur
    Commentaire de xterminhate le 30/12/2004 23:41:49

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

  • signaler à un administrateur
    Commentaire de plus_plus_fab le 02/01/2005 12:46:07

    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.

  • signaler à un administrateur
    Commentaire de xterminhate le 02/01/2005 23:48:30

    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.

Ajouter un commentaire

Pub



Appels d'offres

CalendriCode

Août 2008
LMMJVSD
    123
45678910
11121314151617
18192021222324
25262728293031

Boutique

Boutique de goodies CodeS-SourceS