IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Templates : comment passer un paramètre au constructeur d'une classe utilisant un template ?


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut Templates : comment passer un paramètre au constructeur d'une classe utilisant un template ?
    Bonjour à tous, je découvre les templates depuis hier et je m'ARRACHE les cheveux... Je vais essayer de résumer mon problème, peut-être pourrez-vous m'aider à trouver une solution ou bien m'indiquer un meilleur design. Je poste uniquement du code exemple sinon ce serait trop long à lire.

    J'ai 2 classes : Surface et Media (et demain d'autres), elle ne dérivent pas d'une classe commune et leurs constructeurs n'attendent pas les mêmes paramètres.
    J'ai besoin d'ajouter à ces classe la capacité d'interpoler des valeurs dans le temps. Au début j'ai pensé à l'héritage multiple (dériver Surface et Media d'une classe Interpolator), ça aurait fonctionné sans problème. Mais comme j'ai lu que l'héritage multiple était quelque chose de très mal, j'ai voulu essayer avec des templates.

    Donc j'ai écrit
    - une classe Interpolator
    - un template Interpolable

    Qui sont définis comme ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    struct Interpolator
    {   
       Interpolator(float* theRef) {
       }
     
       Interpolator(int* theRef) {
       }
     
       void update() {}
    };
     
    template <class T>
    struct Interpolable : public T
    {   
       vector<Interpolator> interpolators;
     
       template<typename T2> 
       void interpolate(T2* value) { 
       	Interpolator it(value);
       	interpolators.push_back(it);
       }
     
       void update() {
         T::update();
         while(interpolators.size() > 0)
         for(int i=0; i<interpolators.size(); i++) {
         	interpolators[i].update();
         }
       }
    };
    Je passe sur les détails, ça fonctionne et je l'utilise comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Interpolable<MediaVideo> *mv1 = new Interpolable<MediaVideo>;
    Là ou ça se barre en sucette c'est quand j'utilise autre chose que les constructeurs par défaut, donc si ma classe Media défini un constructeur
    J'ai essayé toute sortes de choses, la plus logique étant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Interpolable<MediaVideo> *mv1 = new Interpolable<MediaVideo>(1);
    J'obtiens invariablement un message d'erreur à la compilation :
    templ2.cpp:96:67: error: no matching function for call to 'Interpolable<MediaVideo>::Interpolable(int)'
    Interpolable<MediaVideo> *mv1 = new Interpolable<MediaVideo>(1);
    ^
    templ2.cpp:96:67: note: candidates are:
    templ2.cpp:73:8: note: Interpolable<MediaVideo>::Interpolable()
    struct Interpolable : public T
    ^
    templ2.cpp:73:8: note: candidate expects 0 arguments, 1 provided
    templ2.cpp:73:8: note: Interpolable<MediaVideo>::Interpolable(const Interpolable<MediaVideo>&)
    templ2.cpp:73:8: note: no known conversion for argument 1 from 'int' to 'const Interpolable<MediaVideo>&'
    Je ne comprends pas ce qui lui pose problème, tout ça est totalement opaque pour moi...

    Merci de votre aide !

  2. #2
    Membre confirmé Avatar de KsassPeuk
    Homme Profil pro
    Ingénieur Chercheur
    Inscrit en
    Juillet 2013
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Chercheur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 138
    Points : 635
    Points
    635
    Par défaut
    Lu'!

    Citation Envoyé par Paganoni Voir le message
    Au début j'ai pensé à l'héritage multiple (dériver Surface et Media d'une classe Interpolator), ça aurait fonctionné sans problème. Mais comme j'ai lu que l'héritage multiple était quelque chose de très mal, j'ai voulu essayer avec des templates.
    Ah bon ? Pourquoi ? L'héritage à outrance fait n'importe comment, c'est mal (typiquement non respect du LSP). Après si Surface ou Media "est un" Interpolator, il n'y a pas de mal à ce que ça en soit (si tu ne veux pas qu'ils sortent du même arbre d'héritage, tu peux utiliser le CRTP).

    La raison pour laquelle ton code ne passe pas est simple : la classe interpolable ne contient pas de constructeur comme celui que tu demandes. Je vois deux solutions à ton problème (d'un point de vue strictement technique), passer l'objet du type paramétrant en paramètre en transmettant la responsabilité à l'objet accueillant, ou alors rendre le constructeur template (variadique si besoin) et transmettre les paramètres au constructeur de l'objet paramétrant dans la liste d'initialisation.

    Mais après, je ne comprends pas le problème que tu veux résoudre (les interpolmachin et les trucs que tu veux faire), et peut être que c'est simplement la conception qui ne convient pas.

    Petite note : les pointeurs nus responsables de la mémoire c'est crado.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut
    Merci de ta réponse, j'en comprends qu'il n'y a pas de solution élégante... Les templates ne sont sans doute pas adaptés à ce que je veux faire...

    J'ai recodé mon bouzin avec l'héritage multiple et c'est :
    - plus simple à écrire
    - plus joli à lire
    - et ça fonctionne

    Oui pour les pointeurs nus, mais la classe Interpolator doit faire varier dans le temps des valeurs de types différents (variables membres de classes Media ou Surface, par ex.) et le truc le plus sympa que j'ai trouvé c'est de stocker des pointeurs vers les variables en question et de les incrémenter ou décrémenter au fil du temps dans la classe Interpolator.

    Mais pour pas faire des type cast dangeureux ou incohérents j'ai fait ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    struct iterValue {
    	enum it_type { t_int, t_float, t_double };
    	int v_type;
    		float * v_f;
    		int * v_i;
    		double * v_d;
    	iterValue(float *value) {
    		v_f = value;
    		v_type = t_float;
    	}
    	iterValue(int *value) {
    		v_i = value;
    		v_type = t_int;
    	}
    	iterValue(double *value) {
    		v_d = value;
    		v_type = t_double;
    	}
    };
     
    void update() {
         cout << "in interpolator update" << endl;
         for(int i=0; i<inter_values.size(); i++) {
         	switch(inter_values[i].v_type) {
         		case iterValue::t_int :
         			(*inter_values[i].v_i)++;
         			break;
         		case iterValue::t_float :
         			(*inter_values[i].v_f)++;
         			break;
         		case iterValue::t_double :
         			(*inter_values[i].v_d)++;
         			break;
         	}     	
         }
       }
    J'aurai sans doute pu arriver au même résultat en utilisant des références au lieu des pointeurs, mais j'ai bien les pointeurs. Je trouve ça moins ambigu à lire.

  4. #4
    Membre confirmé Avatar de KsassPeuk
    Homme Profil pro
    Ingénieur Chercheur
    Inscrit en
    Juillet 2013
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Chercheur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 138
    Points : 635
    Points
    635
    Par défaut
    Citation Envoyé par Paganoni Voir le message
    J'ai recodé mon bouzin avec l'héritage multiple et c'est :
    - plus simple à écrire
    - plus joli à lire
    - et ça fonctionne

    Oui pour les pointeurs nus, mais la classe Interpolator doit faire varier dans le temps des valeurs de types différents (variables membres de classes Media ou Surface, par ex.) et le truc le plus sympa que j'ai trouvé c'est de stocker des pointeurs vers les variables en question et de les incrémenter ou décrémenter au fil du temps dans la classe Interpolator.
    Euh, ça me paraît bizarre ton affaire, on peut voir à quoi ressemble ton code actuel ? Parce que si tu as des fonctions qui agissent différemment, c'est juste des points de variations (et donc du virtual). Donc, il n'y a pas de raison que tu passes sur un truc comme ton IterValue, qui n'est pas propre et pas extensible.

    Citation Envoyé par Paganoni Voir le message
    J'aurai sans doute pu arriver au même résultat en utilisant des références au lieu des pointeurs, mais j'ai bien les pointeurs. Je trouve ça moins ambigu à lire.
    J'ai pas râlé à propos des pointeurs, j'ai râlé à propos de pointeurs nus responsables de la mémoire. Par ailleurs, les références sont sémantiquement bien plus fortes que les pointeurs et il n'y a pas de raison d'utiliser un pointeur à un endroit où l'on attends forcément de trouver un objet.

  5. #5
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 159
    Points
    3 159
    Par défaut
    Hello

    Je vais répondre à plusieurs points. Tout d'abord le premier : tu aurais pu obtenir l'effet recherché dans ta question initiale avec des templates variadiques :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <string>
    #include <utility>
     
    template <typename T> struct Interpolable : public T {
      template <typename ... A> Interpolable(A&& ... args) : T(std::forward<A>(args) ...) {}
    };
     
    struct A {
      A(int) {}
    };
     
    struct B {
      B(std::string const&) {};
    };
     
    int main() {
      Interpolable<A>(1);
      Interpolable<B>("Roger");
      return 0;
    }
    Il existe donc bien une solution élégante pour le faire.

    La solution par héritage dans l'autre sens est bien aussi, surtout si tu comptes à l'avenir leur ajouter d'autre propriétés que l'interpolation. Ce qui est très dommage dans ta solution finale, c'est ton switch sur le type pour utiliser le bon, car tu testes le type au runtime alors que tu pourrais le faire compile-time, c'est justement le type de services que rendent les templates !

    Pour un exemple simpliste:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <string>
    #include <utility>
     
    template <typename T> class Interpolable {
      T value_;
     
     public:
      void update(T value) {
        value = value_;
      }
    };
     
    struct A  : public Interpolable<float> {};
     
    struct B : public Interpolable<double> {};
    Pour les pointeurs, pas d'excuses, surtout si tu adaptes ta solution pour choisir le bon type avec un template.
    Find me on github

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut
    Merci pour ta réponse jb, la syntaxe est un peu raide à piger vu mon niveau de compréhension des templates mais je vais expérimenter dans un source de test (comme je l'ai fait jusqu'à présent).

    Quoi qu'il en soit, j'ai finalement pluggé mon interpolateur dans l'appli que je développe sans douleur, en utilisant l'héritage multiple. Pour les classes existantes je n'ai eu quasiment aucune modification.
    Donc je suis content !

    Ok, je sais que mes bidouilles pour déterminer le bon type sont pas jolies mais je pense qu'elles sont safes.

    Voici interpolation.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
     
    #include <iostream>
    #include <sys/time.h>
    #include "ofMain.h"
     
    class TAInterpoledValue {
    	enum it_type { t_int, t_float };
     
    	int v_type;
    	float *v_f;
    	int *v_i;
     
    	unsigned long long start_time;
    	float startValue;
    	float endValue;
    	int duration;
    	int animationType;
     
    public:
    	enum animation_types {linear, quad_in, quad_out};
     
    	TAInterpoledValue(float *value);
    	TAInterpoledValue(int *value);
     
    	void setToValue(float v);
    	void setToEndValue();
    	float getValue();
     
    	float time_linear(float time);
    	float time_quadraticIn(float time);
    	float time_quadraticOut(float time);
     
    	void setup(int duration, float final, int atype);
    	void setup(int duration, float start, float final, int atype);
    	bool update();
    };
     
    class TAInterpolator
    {
    	vector <TAInterpoledValue> inter_values;
     
    public:
    	template <typename T2>
    	void interpolate(T2 *value, int duration, float final, int atype = 0) {
    		TAInterpoledValue v(value);
     
    		v.setup(duration, final, atype);
    		inter_values.push_back(v);
    	}
     
    	template <typename T2>
    	void interpolate(T2 *value, int duration, float start, float final, int atype = 0) {
    		TAInterpoledValue v(value);
     
    		v.setup(duration, start, final, atype);
    		inter_values.push_back(v);
    	}
     
    	void update() {
    		for (int i = 0; i < inter_values.size(); i++) {
    			if (inter_values[i].update()) {
    				inter_values.erase(inter_values.begin() + i);
    			}
    		}
    	}
    };
    et interpolation.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
     
    #include "TAInterpolation.h"
     
    TAInterpoledValue::TAInterpoledValue(float *value) {
    	v_f = value;
    	v_type = t_float;
    }
     
    TAInterpoledValue::TAInterpoledValue(int *value) {
    	v_i = value;
    	v_type = t_int;
    }
     
    void TAInterpoledValue::setToValue(float v) {
    	switch (v_type) {
    		case t_int:
    			(*v_i) = v;
    			break;
     
    		case t_float:
    			(*v_f) = v;
    			break;
    	}
    }
     
    void TAInterpoledValue::setToEndValue() {
    	setToValue(endValue);
    }
     
    float TAInterpoledValue::getValue() {
    	switch (v_type) {
    		case t_int:
    			return *v_i;
    			break;
     
    		case t_float:
    			return *v_f;
    			break;
    	}
    }
     
    float TAInterpoledValue::time_linear(float time) {
    	return time;
    }
     
    float TAInterpoledValue::time_quadraticIn(float time) {
    	return time * time;
    }
     
    float TAInterpoledValue::time_quadraticOut(float time) {
    	return time * (2 - time);
    }
     
    void TAInterpoledValue::setup(int duration, float final, int atype) {
    	this->start_time = ofGetElapsedTimeMillis();
    	this->duration = duration * 1000;
    	this->startValue = getValue();
    	this->endValue = final;
    	this->animationType = atype;
    }
     
    void TAInterpoledValue::setup(int duration, float start, float final, int atype) {
    	this->startValue = start;
    	setup(duration, final, atype);
    }
     
    bool TAInterpoledValue::update() {
    	unsigned long long elapsed = ofGetElapsedTimeMillis() - start_time;
    	if (elapsed >= duration) {
    		setToEndValue();
    		return true;
    	}
    	else {
    		float time = (float)elapsed / (float)this->duration;
    		float time_computed;
    		switch (this->animationType) {
    			case linear:
    			default:
    				time_computed = time_linear(time);
     
    			case quad_in:
    				time_computed = time_quadraticIn(time);
     
    			case quad_out:
    				time_computed = time_quadraticOut(time);
    		}
    		float v = this->startValue + (this->endValue - this->startValue) * time_computed;
    		setToValue(v);
    		return false;
    	}
    }
    Du coup du côté des classes Media et Surface je peux écrire
    interpolate(&opacity, 10, 1.0f);
    interpolate(&volume, 10, 0.0f);

    Qu'il s'agisse de floats (opacité) ou d'int (volume)

    Tout ce qui a changé dans mes deux classes c'est
    - hériter de public TAInterpolator
    - ajouter TAInterpolator::update() dans les méthodes update() de Surface et Media

    Et ça roule !!!

    Je vais essayer de me creuse la tête pour que TAInterpoledValue utilise un template pour virer ce qui existe actuellement, et je garderai cependant le principe de l'héritage multiple...

  7. #7
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 159
    Points
    3 159
    Par défaut
    Hello

    En lisant ton code, je comprend mieux.

    Déjà, sur l'histoire des pointeurs, peut-importe la justification, le fait que l'interpolateur utilise des pointeurs devrait rester un détail d'implémentation. En d'autres termes, dans les fonctions membres interpolate, tu passes en argument une référence et tu prend l'adresse, si tu en as effectivement besoin, dans l'implémentation. Ce n'est pas à l'utilisateur de l'interpolateur d'être impacté par ça.

    Plusieurs questions, pour moi, se posent:

    1. Est-ce que dans un seul objet, certaines variables interpolées seront entières et d'autres flottantes ?
    2. Est-il vraiment utile de supprimer des éléments du tableau ?
    3. L'héritage rend-t-il un service ?


    Point 1

    Si dans un objet, un seul type de donnée est interpolé, alors l'interpolateur doit être templaté pour n'accepter et traiter que ce type. Si dans un seul objet, certaines variables qui doivent être interpolées sont entières et d'autres flottantes, je ne vois pas l'intérêt de rajouter un mécanisme de typage dynamique : il devrait y avoir un tableau pour les entiers et un autre pour les flottants, le tout géré avec des surcharges et pas avec des fonctions membres templates.

    Point 2

    Je vois que tu supprimes des éléments dans le tableau. Tout d'abord, ton code ne fonctionne pas bien, voir l'idiome erase/remove. Et deuxièmement, si lorsque tu arrives à l'instant t qui signifie la fin de l'interpolation, à quoi cela sert-il de faire un setToEndValue() si c'est pour la supprimer juste après ? Si tu dois effectivement supprimer ces valeurs, est-ce que ça ne veut pas dire que ton objet est en fin de vie, et que par conséquent, il te suffit le laisser le soin au destructeur de vider le vecteur ? Il y a un problème de conception ici.

    J'irais même plus loin en disant que selon moi, tu devrais utiliser des tableaux de tailles fixes (std::array), taille qui est passée en paramètre template par la classe utilisatrice, car la classe en question sait à l'avance combien de variables seront interpolées.

    Point 3

    Celui-ci risque de te chiffonner . Ce que je constate, c'est qu'il n'y a pas de fonctions membres publiques d'accès au données résultantes de l'interpolation. Il n'y a que interpolate et update. Soit ces accesseurs publics ne sont pas publiés pour limiter le code sur le forum (dans ce cas bien vu, mais ajoute un petit commentaire dans le code qui le précise ), soit il n'y pas d'intérêt à utiliser de l'héritage ici. Dans ce dernier cas, il vaut mieux travailler par composition, c'est à dire, ajoute une donnée membre de type Interpolator dans les classes qui s'en servent.



    Il y a encore un peu de boulot, mais tu ne regretteras d'avoir été attentif à ta conception en début de projet
    Find me on github

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut
    Alors, concernant les points 2 & 3 (setEndValue et pourquoi uniquement update et interpolate) : parce que mon code modifie la valeur d'origine de la variable passée en paramètre, à la manière d'une référence mais avec des pointeurs... Le setEndValue est utilisé pour s'assurer que la valeur finale est bien la valeur finale demandée, au cas où une approximation du timing laisserait la variable dans un état proche, mais non égal à la valeur attendue (0.999999 au lieu de 1 par ex.)

    Mais la bonne nouvelle c'est que j'ai ENFIN réussi à réecrire le code en utilisant un template ET des références (ce dernier point n'était pas le plus compliqué, 3 modifs et c'était ok)

    Non, le truc sur lequel j'ai bloqué pendant des jours c'était comment utiliser un template de classe et un vector, sachant qu'on peut pas mélanger les deux ! Et putain, la feinte mafieuse -> déclarer une classe ancêtre, templatiser une classe dérivée et utiliser le vector avec la classe ancêtre !!!!!

    J'AI PASSE 2 JOURS LA DESSUS !!! Enfin la lumière !!!

    Et du coup, plus qu'un seul fichier Interpolator.h bien plus lisible, bien plus court. C'est super, en un mot : Youpi !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
     
    class ITAInterpoledValue {
    public:
    	enum animation_types {linear, quad_in, quad_out};
     
    protected:
    	float startValue;
    	float endValue;
    	int duration;
    	int animationType;
    	unsigned long long start_time;
     
    	float time_linear(float time) {
    		return time;
    	}
     
    	float time_quadraticIn(float time) {
    		return time * time;
    	}
     
    	float time_quadraticOut(float time) {
    		return time * (2 - time);
    	}
     
    public:
    	virtual void setup(int duration, float start, float final, int atype) = 0;
    	virtual void setup(int duration, float final, int atype) = 0;
    	virtual bool update() = 0;
    };
     
    template <class T>
    class TAInterpoledValue : public ITAInterpoledValue {
    	T &v;
     
    public:
    	TAInterpoledValue(T & value) : v(value)
    	{
    	}
     
    	void setToValue(float va) {
    		v = va;
    	}
     
    	void setToEndValue() {
    		setToValue(endValue);
    	}
     
    	float getValue() {
    		return v;
    	}
     
    	void setup(int duration, float final, int atype) {
    		this->start_time = ofGetElapsedTimeMillis();
    		this->duration = duration * 1000;
    		this->startValue = getValue();
    		this->endValue = final;
    		this->animationType = atype;
    	}
     
    	void setup(int duration, float start, float final, int atype) {
    		this->startValue = start;
    		setup(duration, final, atype);
    	}
     
    	bool update() {
    		unsigned long long elapsed = ofGetElapsedTimeMillis() - start_time;
    		if (elapsed >= duration) {
    			setToEndValue();
    			return true;
    		}
    		else {
    			float time = (float)elapsed / (float)this->duration;
    			float time_computed;
    			switch (this->animationType) {
    				case linear:
    				default:
    					time_computed = time_linear(time);
     
    				case quad_in:
    					time_computed = time_quadraticIn(time);
     
    				case quad_out:
    					time_computed = time_quadraticOut(time);
    			}
    			float v = this->startValue + (this->endValue - this->startValue) * time_computed;
    			cout << "v:" << v << " time:" << elapsed << " start:" << startValue << " end:" << endValue << " duration:" << duration << endl;
    			setToValue(v);
    			return false;
    		}
    	}
    };
     
    class TAInterpolator
    {
    	vector <ITAInterpoledValue *> inter_values;
     
    public:
    	template <typename T2>
    	void interpolate(T2 &value, int duration, float final, int atype = 0) {
    		TAInterpoledValue <T2> *v = new TAInterpoledValue <T2>(value);
     
    		v->setup(duration, final, atype);
    		inter_values.push_back(v);
    	}
     
    	template <typename T2>
    	void interpolate(T2 &value, int duration, float start, float final, int atype = 0) {
    		TAInterpoledValue <T2> *v = new TAInterpoledValue <T2>(value);
     
    		v->setup(duration, start, final, atype);
    		inter_values.push_back(v);
    	}
     
    	void update() {
    		for (int i = 0; i < inter_values.size(); i++) {
    			if (inter_values[i]->update()) {
    				delete inter_values[i];
    				inter_values.erase(inter_values.begin() + i);
    			}
    		}
    	}
    };
    Le seul truc que je comprends pas bien et que j'ai trouvé en tâtonnant, c'est ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TAInterpoledValue <T2> *v = new TAInterpoledValue <T2>(value);
    La syntaxe m'apparait bizarre, naturellement j'aurais imaginé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TAInterpoledValue *v = new TAInterpoledValue (T2 value);
    Je ne saisi pas bien la nuance entre le <T2> de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TAInterpoledValue <T2> *v = new TAInterpoledValue <T2>(value);
    et le T2 de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void interpolate(T2 &value, int duration, float final, int atype = 0)

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 626
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    Salut,
    Heuu, juste un dernier point au passage... : les fonctions setup, c'est crado: cela implique que tu peux créer des objets que tu ne peux pas utiliser directement (pas tant que la fonction setup n'a pas été appelée).

    Pire encore : cela implique que, avant de pouvoir utiliser un objet que tu auras créé (ou dont tu auras délégué la construction à "autre chose"), il faudra que tu t'assures que la fonction setup ait bel et bien été appelée. Et,... que se passe-t-il si on appelle setup sur un objet sur lequel elle a déjà été appelée Cela a-t-il du sens de l'admettre si cela n'en a pas, comment vas-tu faire pour éviter que cela n'arrive

    Non, décidément, quand tu crées un objet, tu dois veiller à ce qu'il soit directement utilisable. Et il est super facile d'y arriver car c'est justement le rôle d'une fonction toute particulière que l'on appelle le constructeur

    Tu devrais donc envisager de remplacer tes fonctions setup par des constructeurs, sous une forme qui sera sans doute proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class ITAInterpoledValue {
    public:
    	enum animation_types {linear, quad_in, quad_out};
    	ITAInterpoledValue (int duration, float start, float final, int atype)
            ITAInterpoledValue (int duration, float final, int atype);
    	/* ... */
    };
     
    template <class T>
    class TAInterpoledValue : public ITAInterpoledValue {
    	T &v;
     
    public:
    	TAInterpoledValue(int duration, float start, float final, int atype,T & value) :
                 ITAInterpoledValue (duration, start, final, atype), v(value){}
     
    	TAInterpoledValue(int duration, float final, int atype,T & value) :
                ITAInterpoledValue (duration, finale, atype), v(value){
            }
    };
    Alors, bien sur, cela t'oblige à retarder la création de l'objet au moins jusqu'à ce que tu connaisse les valeurs de duration, start, final et atype, mais ca, c'est aussi une bonne chose
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut
    Hello, oui ce que tu dis a du sens...

    En fait au lieu de deux constructeurs
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ITAInterpoledValue (int duration, float start, float final, int atype)
            ITAInterpoledValue (int duration, float final, int atype);
    Je pourrais avoir la première forme uniquement et dans la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void interpolate(T2 &value, int duration, float final, int atype = 0) {
    initialiser start et passer l'ensemble des paramètres au constructeur...

    Il y a un truc que j'aimerais bien, mais ça amène à un niveau de compréhension que je n'ai pas, c'est remplacer l'exploitation de références par un appel à une méthode passée en callback. Sachant que les méthodes de callback pourraient recevoir différents types de paramètres (float) (int) (Vector3) (Vector2)

    En clair, au lieu de passer la référence d'une variable de la classe dans laquelle écrire la valeur interpolée, je passerai l'adresse d'une fonction qui (elle) stockerait la valeur dans un membre de la classe... Du coup ça ouvrirait de nouveaux horizons (la fonction en question pourrait exploiter les données pour en faire quelque chose, balancer la valeur sur le réseau par exemple) - Mais à écrire avec des templates c'est l'horreur absolue, j'ai eu beau chercher j'ai pas trouvé..

    Il y a ce lien ici https://juanchopanzacpp.wordpress.co...mentation-c11/

    Ca fonctionne nickel, mais absolument pas sur des fonctions de classes... Ni même si le callback en question a plusieurs signatures (enfin, plusieurs versions de la même méthode acceptant différents paramètres)

  11. #11
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 159
    Points
    3 159
    Par défaut
    Hello

    Je vois plein d'améliorations possibles:
    • L'algo update devrait utiliser l'idiome erase/remove avec une lambda, cette boucle "à la main" est dangereuse.
    • Les pointeurs vers TAInterpoledValue devraient être remplacé par des std::unique_ptr
    • Le membre atype est inutile et pourrait être remplacé par un argument template supplémentaire, afin d'économiser le switch
    • Comme conseillé par Koala, les fonctions setup devraient disparaître au profit de constructeurs.
    • duration est int alors que tes mesure de temps sont en unsigned long long. Il faudrait les mettre en cohérence.
    • Même remarque pour startValue, endValue et [v] : elles devraient toutes être du même type.
    • Les cast c-style devraient être remplacés par des cast C++
    • this peut être retiré à beaucoup d'endroits
    • J'ai vu cout tout seul sans son std : attention au using namespace, jamais dans un header (exceptionnellement dans des templates, scopé).
    • Il est possible de faire ce que tu veux avec des callbacks, si c'est clair dans ta tête, avec std::function ça se fait bien.


    Je te laisse un peu réfléchir dessus et je reviens avec des exemples
    Find me on github

  12. #12
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 159
    Points
    3 159
    Par défaut
    Hello

    Normalement, cela ne se fait pas trop d'écrire des solutions à la place de l'op. En l'occurence, j'ai pris le code que tu as posté et j'ai "simplement" appliqué mes remarques, parce que c'est plus simple que de montrer un exemple pour chacune d'entre elles indépendamment. Le seul changement structurel que j'ai effectué est d'avoir mis les membres dans l'implémentation et pas dans l'inerface. Je pense qu'ils ne doivent pas être dans l'inerface puisque le seul code qui s'en sert est dans l'implémentration. Du reste, il peut dépendre du type template T choisir. Voici:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    // Time computation functions
    enum class animation_types {linear, quad_in, quad_out};
    template<animation_types AnimType> float getComputedTime(float time);
    template <> float getComputedTime<animation_types::linear>(float time) { return time; }
    template <> float getComputedTime<animation_types::quad_in>(float time) { return time*time; }
    template <> float getComputedTime<animation_types::quad_out>(float time) { return time*(2.f-time); }
     
    // Interface
    struct ITAInterpoledValue {
      virtual ~ITAInterpoledValue() {}
      virtual bool update() = 0;
    };
     
    template <animation_types AnimType, typename T> class TAInterpoledValue : public ITAInterpoledValue {
      T &v;
      T startValue;
      T endValue;
      unsigned long long duration;
      unsigned long long start_time;
     
     public:
      TAInterpoledValue(T& value, unsigned long long iDuration, T iStart, T iFinal) : 
        v(value)
        , startValue(iStart)
        , endValue(iFinal)
        , duration(1000u*iDuration)
        , start_time(ofGetElapsedTimeMillis())
      {}
     
      TAInterpoledValue(T& value, unsigned long long iDuration, T iFinal) : 
        TAInterpoledValue(value, iDuration, value, iFinal)
      {}
     
      void setToValue(T va) { v = va; }
      void setToEndValue() { setToValue(endValue); }
     
      bool update() override {
        unsigned long long elapsed = ofGetElapsedTimeMillis() - start_time;
        if (elapsed >= duration) {
          setToEndValue();
          return true;
        }
        else {
          float time = static_cast<float>(elapsed) / static_cast<float>(duration);
          float time_computed = getComputedTime<AnimType>(time);
          T v = static_cast<T>(static_cast<float>(startValue) + static_cast<float>(endValue - startValue) * time_computed);
          //cout << "v:" << v << " time:" << elapsed << " start:" << startValue << " end:" << endValue << " duration:" << duration << endl;
          setToValue(v);
          return false;
        }
      }
    };
     
    class TAInterpolator {
      std::vector <std::unique_ptr<ITAInterpoledValue>> inter_values;
     
     public:
      // Template variadic pour supporter toutes les signatures
      template <animation_types AnimType, typename T, typename ... Args>
        void interpolate(T& value, Args&& ... args) {
          inter_values.emplace_back(new TAInterpoledValue<AnimType,T>(value, std::forward<Args>(args)...));
        }
     
      void update() {
        inter_values.erase(
            std::remove_if(begin(inter_values) , end(inter_values)
              , [](std::unique_ptr<ITAInterpoledValue>& v) { return v ? v->update() : false; })
            , end(inter_values));
      }
    };
    En théorie, ça doit faire exactement la même chose (en moins de lignes) et ça devrait se mettre dans ton code actuel sans changement pour le reste au temps pour moi, la signature de interpolate change à cause de l'évolution sur le choix de l'animation. Je te laisse jeter un oeil, on traitera les callbacls plus tard . Le code est testé et il ne fuit pas.
    Find me on github

Discussions similaires

  1. Réponses: 15
    Dernier message: 07/06/2011, 22h42
  2. Comment passer des paramètre a OpenRecordset
    Par molarisapa dans le forum Access
    Réponses: 2
    Dernier message: 09/03/2006, 17h14
  3. Réponses: 3
    Dernier message: 28/02/2006, 08h43
  4. [VB6] Comment passer un paramètre à un vbs depuis du vb6
    Par durnambule dans le forum VBScript
    Réponses: 2
    Dernier message: 27/09/2005, 10h46
  5. Réponses: 7
    Dernier message: 30/12/2004, 12h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo