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 :

Typages dynamiques à l'exécution


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut Typages dynamiques à l'exécution
    Bonjour à tous,

    Dans la construction de mon jeu, j'essaie d'atteindre un degré d'abstraction que je n'arrive pas à obtenir avec mes connaissances. C'est à cause des types de variables en statique dans le code et j'essaie de le rendre dynamique et donc les types sont connus uniquement à l'exécution.

    Pour vous expliquer le contexte, c'est des caractéristiques d'entités et j'ai créé une classe caractéristique qui est censé contenir une variable de type dépendant d'un fichier XML, pour que ça soit plus facilement modifiable sans toucher au code à chaque fois. J'ai utilisé boost::any pour l'instant mais je me retrouve bloqué: en ce moment dans mon fichier XML je spécifie le type de variable (ex: unsigned int), je le parse et je fais pleins de if (voir dernier code) pour traiter tous les cas de types (7) dont j'ai besoin pour le moment.

    En plus je vais devoir les manipuler (ces variables dynamiques), je vais devoir "additionner" 2 caractéristiques ensemble (pour les niveaux des entités) uniquement si le type concorde (Est-ce que boost::any supporte l'operator+() ?). A noter que le type d'addition est spécifié par le fichier XML. Ensuite je dois pouvoir afficher cette variable dans mon code avec son bon type, pour l'utiliser dans le gameplay (par exemple les HP, la vitesse d'attaque etc... sont des caractéristiques en unsigned int). Voilà la partie du code intéressant
    characteristic.h :

    Code CPP : 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
    class Characteristic {
    	public:
    		// Constructors
    		Characteristic(string _charactName, boost::any _val);
    		~Characteristic();
    		// Functions
    		void setUpDownOperators(string _upOper, string _dwOper);
    		// Operators
    		Characteristic doOperator(string _currOp, const Characteristic &upgrader);
    		Characteristic operator+(const Characteristic &upgrader);
    		Characteristic operator-(const Characteristic &downgrader);
    		// Getters
    		unsigned int getUint();
    		signed int getSint();
    		float getF();
    		double getD();
    		bool getB();
    		string getStr();
    		entityRunnerFlags getErf();
    		entityTowerFlags getEtf();
    		damageType getDt();
     
    	private:
    		string charactName;
    		string upOper;
    		string dwOper;
    		boost::any val;
     
    };

    characteristic.cpp :
    Code CPP : 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
    // Constructors for each type
    Characteristic::Characteristic(string _charactName, boost::any _val) : charactName(_charactName), val(_val) { }
    Characteristic::~Characteristic() { }
     
    void Characteristic::setUpDownOperators(string _upOper, string _dwOper) {
    	upOper = _upOper;
    	dwOper = _dwOper;
    }
     
    // Operators
    Characteristic Characteristic::doOperator(string _currOp, const Characteristic &upgrader) {
    	if (_currOp.empty())
    		return *this;
    	if (val.type() != upgrader.val.type())
    		return *this;
     
    	if (_currOp == string("+")) {
    		val += upgrader.val;
    	}
    	else if (_currOp == string("-")) {
    		val -= upgrader.val;
    	}
    	else if (_currOp == string("0")) {
    		val = upgrader.val;
    	}
    	else if (_currOp == string("-1")) {
    		// Do nothing
    	}
    	else {
    		// Do nothing
    	}
     
    	return *this;
    }
     
    Characteristic Characteristic::operator+(const Characteristic &upgrader) {
    	return doOperator(upOper, upgrader);
    }
     
    Characteristic Characteristic::operator-(const Characteristic &downgrader) {
    	return doOperator(dwOper, downgrader);
    }
     
    // Getters
    unsigned int Characteristic::getUint() {
    	return boost::any_cast< unsigned int >(val);
    }
     
    signed int Characteristic::getSint() {
    	return boost::any_cast< signed int >(val);
    }
     
    float Characteristic::getF() {
    	return boost::any_cast< float >(val);
    }
     
    double Characteristic::getD() {
    	return boost::any_cast< double >(val);
    }
     
    bool Characteristic::getB() {
    	return boost::any_cast< bool >(val);
    }
     
    string Characteristic::getStr() {
    	return boost::any_cast< string >(val);
    }
    entityRunnerFlags Characteristic::getErf() {
    	return boost::any_cast< entityRunnerFlags >(val);
    }
     
    entityTowerFlags Characteristic::getEtf() {
    	return boost::any_cast< entityTowerFlags >(val);
    }
     
    damageType Characteristic::getDt() {
    	return boost::any_cast< damageType >(val);
    }

    un BOUT du .XML (avec les 3 façons d'additionner)
    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    <?xml version="1.0" encoding="UTF-8"?>
    <EntityCharacteristicsList>
    	<maxHp type="unsigned int" upgradeOperator="+" downgradeOperator="-">5</maxHp>
    	<moveRate type="unsigned int" upgradeOperator="-" downgradeOperator="+">0</moveRate>
    	<text type="string" upgradeOperator="0" downgradeOperator="0">hello</text>
    </EntityCharacteristicsList>

    Et pour finir, le chargeur du fichier XML qui est censé après l'avoir chargé être capable de lire la variable sans connaître son type (en le récupérant depuis la classe characteristic par exemple) :
    Code CPP : 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
    bool loadCharactListFile(string filePath) {
    		[...]
    		// partie chargement du XML omise
     
    		// Définition: string currType, currName
    		// Try to convert to the right type
    		Characteristic *currCharact;
    		// -- unsigned int
    		if (currType == string("unsigned int")) {
    			unsigned int val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, val);
    		}
    		// -- signed int
    		else if (currType == string("signed int")) {
    			signed int val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, val);
    		}
    		// -- unsigned int - damageType
    		else if (currType == string("damageType")) {
    			unsigned int val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, convertAttackTypeFile( val ));
    		}
    		// -- unsigned int - entityRunnerFlags
    		else if (currType == string("entityRunnerFlags")) {
    			unsigned int val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, convertRunnerFlagsTypeFile( val ));
    		}
    		// -- unsigned int - entityTowerFlags
    		else if (currType == string("entityTowerFlags")) {
    			unsigned int val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, convertTowerFlagsTypeFile( val ));
    		}
    		// -- bool
    		else if (currType == string("bool")) {
    			bool val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, val);
    		}
    		// -- string
    		else {
    			string val;
    			current->GetValue(&val);
    			currCharact = new Characteristic(currName, val);
    		}
    		charactList.push_back( *currCharact );
    		delete currCharact;
     
    		// Try to get operators
    		try {
    			string upOper, downOper;
    			upOper = currEl->GetAttribute("upgradeOperator");
    			downOper = currEl->GetAttribute("downgradeOperator");
    			charactList.at(charactList.size() - 1).setUpDownOperators(upOper, downOper);
    		}
    		catch (ticpp::Exception& ex) {
    			cout << "Cannot get "<<child->Value()<<" operators"<<endl;
    			return false;
    		}
    	}
     
    	cout << "Test values :"<<endl;
    	for (unsigned int i = 0; i < charactList.size(); i++) {
    		try {
    			cout << "["<<i<<"] "<< charactList.at(i).getEtf() <<endl;
    		}
    		catch (boost::bad_any_cast& bac) {
    			cout << "Bad type given ! ("<<bac.what()<<")"<<endl;
    		}
    	}
    }

    J'espère que j'ai été suffisamment explicite dans mon problème et je sais que c'est pas toujours aisé de comprendre le code des autres, j'ai fais du mieux que j'ai pu. J'espère que vous pourrez m'aider, merci d'avance !!

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Salut,

    Peut-être devrais-tu essayer d'interfacer avec un langage de script ? LUA par exemple à le vent en poupe dans les studios de jeux vidéo ces derniers temps.

    MAT.

  3. #3
    Membre régulier Avatar de cynique
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 60
    Points : 72
    Points
    72
    Par défaut
    C'est trop compliqué.

    Pendant l'éxécution du code, tu as besoin de variables simples, sans cast, sans operator +() explicit, etc.

    Si tu utilisais cette classe pour les variables vifs, tu tuerais le perf à cause de plusieurs vérifications de concorde de type, et le type ne change pas après la chargement.

    Tu as créé une classe qui peut être n'importe quoi, mais chaque variable de cette class doit être une seule chose. Il me semble que ce sera mieux avec des variables simples, int, unsigned, double, string, etc.

    Tout ça, c'est à mon avis...

  4. #4
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Bonjour,

    J'ai déjà réalisé un système un peu dans ce genre.

    Voilà comment j'ai procédé :

    - une classe de base "objetDynamique"
    - toutes les méthodes utiles (addition, opérations...) déclarées en virtuelle pur dans cette classe
    - une methode "getType()" en virtuel pur dans cette classe
    - une classe "Factory" capable de fabriquer un objet à partir de son type, avec une fonction du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    objetDynamique* createObject(std::string type)
    Ensuite :

    - une classe dérivée pour chaque type "concret" de donnée : par exemple,
    - une classe pour les entier
    - une classe pour les flottant
    - une classe pour les chaines
    - ...
    (la methode getType() de ces classes renvoit leur type en std::string)

    Lors de la lecture du fichier XML, l'appel à la Factory permet de creer les bons objets. Ensuite, dans le code, on agit que sur l'interface de la classe "objetDynamique" (tous les pointeur dans le code sont de ce type)


    Pour gerer correctement les conversions entre objets, on peut aussi faire appel à la factory. Voila comment j'ai fait :

    - une classe mère "AbstractFactory" qui contient les fonctions virtuelles pures :
    - objetDynamique* createObject();
    - int toInt();
    - double toDouble();
    - std::string toString();
    - ....

    - pour chaque type concret, cette classe est dérivées et implémentée

    - la factory "principale" (celle du début) contient alors une map avec comme clé le type en std::string et en valeur un pointeur vers la factory correspondante. On enregistre une nouvelle factory avec un "register" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    map<string, AbstractFactory*> m_factoriesMap;
    registerFactrory(string type, AbstractFactory*factory);
    Comme ça :

    - au début du code, on fait un register pour chaque type, avec sa factory associée.

    - la factory "principale" est un singleton, et on peut l'appeler de partout, par exemple pour creer un objet, on appel "createObject(type)" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    objetDynamique* createObject(std::string type)
    {
        AbstractFactory* pFactory = m_factoriesMap[type];
        if (pFactory)
        {
            return pFactory->createObject();
        }
        return NULL;
    }
    Pour convertir un objet :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    int toInt(objetDynamique* pObjet, int valeurDefaut)
    {
        if (pObjet)
        {
            AbstractFactory* pFactory = m_factoriesMap[pObjet->getType()];
            if (pFactory)
            {
                return pFactory->toInt(pObjet);
            }
        }
     
        return valeurDefaut;
    }

  5. #5
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    J'ai regardé en vitesse, mais ça me semble un cas d'utilisation typique d'un variant.

    Auquel cas, regarde du côte de boost::variant, ça devrait répondre à ton besoin.

  6. #6
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Pour ma part, la solutoin dont je parle plus haut est basé sur des QVariant (les variants de Qt)

    Avec des Boost::Variant, je suppose que c'est à peu près la même chose.

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut
    @Mat007

    Salut, ça pourrait être intéressant mais j'ai des raisons de penser que ce sera plus "simple" ou plutôt rapide si je reste en C++ et je préférerais.

    @buzzkaido

    Salut, faut s'accrocher ! J'avoue que j'ai eu de la peine à comprendre, j'ai dû m'y reprendre à plusieurs fois pour vraiment saisir et il y a encore certains points que je n'ai pas bien saisi. Je te remercie déjà de l'effort que tu as fait en détaillant une partie de la solution, mais il me faudrait encore quelques détails si je puis me permettre.
    Notamment les variables privées de la classe objetDynamique, ou plutôt des classes dérivées: c'est bien ces dernières qui vont stockés la variable c'est bien ça ? Et donc chaque sous-classe contiendra chacune un type de variable différent. Soit admettons objetDynamiqueInt, objetDynamiqueFloat etc... si j'ai bien suivis ? Ça donnerait quelque chose du style (non-testé) :

    Code cpp : 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
    class objetDynamique {
    	public:
    		virtual string getType();
    		virtual operator+(objetDynamiqueInt& _object);
    		virtual operator-(objetDynamiqueInt& _object);
     
    	private:
    		// Rien
    }
     
    class objetDynamiqueInt : public objetDynamique {
    	public:
    		string getType();
    		// Opérations sans tenir compte de l'XML pour l'instant !
    		operator+(objetDynamiqueInt& _object);
    		operator-(objetDynamiqueInt& _object);
     
    	private:
    		unsigned int value;
    }
     
    string objetDynamiqueInt::getType() {
    	return string("unsigned int");
    }
     
    operator+(objetDynamiqueInt& _object) {
    	value += _object.value;
    }
     
    operator-(objetDynamiqueInt& _object) {
    	value -= _object.value;
    }

    Quelque chose ainsi je pense. Mais peut-être que ce serait mieux si c'est Factory qui se charge des additions/soustractions de deux objets objetDynamique entre eux, c'est lui qui gère le bon type de variable, et qu'il faut que ce type corresponde entre les deux pour faire l'opération (qui est défini dans l'XML). Petite précision quand je dis operator+ cela correspond à l'upgrade soit l' "AJOUT" de l'attribut pour que l'entité soit plus fort. Cela dépend donc du type de caractéristique (si c'est un rate faudra soustraire lors d'un operator+ par exemple).

    Donc si j'ai bien compris ton map m_factoriesMap correspond à un indexeur de constructeur de classe pour une variable de type passé en string. Mais donc il y a une seule classe AbstractFactory correspondant à chaque type de variable. De plus la classe Factory me permettrait de créer la bonne variable pour la donner à GetValue() c'est bien ça ?

    J'aimerais savoir également comment avec cette technique faire un cout sur un tableau de ces caractéristiques une fois ces valeures extraites du XML et mises dans un vector (ou map ce serait mieux sûrement) ? Pour que ce soit le bon type qui soit utilisé (sachant que Cout reconnait seul la plupart des types, on peut juste lui afficher la valeure depuis la classe et faire: objetDynamique::getValue() je suppose ?

    De manière générale tu m'as donné une solution mais il me manque les réponses ci-dessus pour l'appliquer à mon cas. Je ne sais pas encore comment l'intégrer à mon jeu totalement. Sinon merci encore de la peine que tu as pris pour ton post instructif !

    Autrement s'il y a une solution avec boost::variant qui pourrait me convenir, je suis partant, par contre je débute avec cette librairies car elle est nouvelle pour moi. Il va me falloir une piste pour l'utiliser. Sinon je pense qu'on peut se passer de boost et utiliser la technique de buzzkaido.

  8. #8
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Globalement, ma solution consiste à :

    - créer une classe dérivée de objetDynamique pour chaque type (objetDynamiqueInt, objetDynamiqueDouble...)

    - implementer dans chacune de ces classes les operations qui leur sont specifiques

    D'un autre coté :

    - créer une "factory" pour chaque type (factoryInt, factoryDouble...)

    - implementer dans chaque factory :
    - une methode de construction
    - des methodes de conversion (toInt, toDouble...)

    Ensuite, toutes les factories sont regroupées dans une "std::map" qui permet de recuperer la bonne factory associée à un type (ici, une chaine de caractere)

    Pour l'utiliser :

    - creation d'une instance d'un objet : tu lis le type dans le XML, tu appelle le "createObject" sur la factory globale avec ce type : celle-ci s'occupe de recuperer la bonne factory et d'appeler le "createObject" dessus

    - lorsque tu implementes une addition sur l'objet objetDynamiqueInt par exemple, ça se passe comme ça : la methode "add" est declarée virtuelle pure dans la classe mere)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    objetDynamiqueInt::add(objetDynamique* pOther)
    {
        // Conversion de l'objet passé en parametre en un objet utilisable par celui-ci
        objetDynamiqueInt otherInt = Factory::getInstance()->toInt(pOther);
     
        // On fait l'operation
        m_value = m_value + otherInt.value();
    }
    Comme ça, ton "add" effectue toujours la meme operation, il suffit juste que le code de conversion dans la factory renvoit une valeur utilisable. (ou un code d'erreur si la conversion n'est pas possible)

  9. #9
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Euh... je viens de relire l'ensemble...

    Je suis pas sûr de bien comprendre...

    Citation Envoyé par jamesb Voir le message
    Mais peut-être que ce serait mieux si c'est Factory qui se charge des additions/soustractions de deux objets objetDynamique entre eux, c'est lui qui gère le bon type de variable, et qu'il faut que ce type corresponde entre les deux pour faire l'opération (qui est défini dans l'XML). Petite précision quand je dis operator+ cela correspond à l'upgrade soit l' "AJOUT" de l'attribut pour que l'entité soit plus fort. Cela dépend donc du type de caractéristique (si c'est un rate faudra soustraire lors d'un operator+ par exemple).
    Tu cherche à faire quoi exactement ?

    Ajouter dynamiquement des "membres" à ta classe ?
    Ou additionner / soustraire des valeurs ?

    Car dans le premier cas, il te suffit que ta classe ait un vecteur de objetDynamique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<objetDynamique*> m_proprietes;
    Et ensuite, lors de la lecture du XML, tu te sers de la factory pour fabriquer les bons objets et tu les inseres dans le vecteur.

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut
    Citation Envoyé par buzzkaido Voir le message
    Ajouter dynamiquement des "membres" à ta classe ?
    Ou additionner / soustraire des valeurs ?.
    Je dirais additionner / soustraire les valeurs. Si je détaille un peu plus, le fichier XML que l'on a au-dessus est en fait la template de BASE que chaque entité aura par défaut. Après ça, il y a aura des templates dérivée pour chaque entitée qui "écrasera" ce qui n'a pas été affecté par la template de base. Le but de la template de base est de donner :
    - la liste des caractéristiques que chaque entité POURRA avoir (comme Warcraft3 pour les connaisseurs)
    - le type de variable que cette caractéristique aura en C++ (par conversion)
    - Le type d'opération qu'il faut appliquer (+ / - / affectation / rien) pour faire progresser l'entité du niveau n au niveau n+1 et inversément du niveau n à n-1 (ce que j'appelle upgrader et downgrader l'entité respectivement). Ces templates dérivées seront traitées dès que j'aurai réglé le problème avec la template de base.

    Pour parler des templates dérivées voici un exemple (niveau 0 complète et niveaux 1 et les suivants sont incrémentiels).
    Nouvelle entité -> utiliser la template de base (copiée dans la template de l'entité qui a une instance indépendante) -> appliquer le level 0 de la template propre à l'entité sur la template de l'entité (affectation car upgrade=0; voir ci-dessous). Puis ensuite le joueur fait progresser son entité en changeant de niveau et donc le jeu fait un upgrade (donc additionner chaque caractéristique de l'entité). Extrait de la template des caractéristiques d'une entité :
    Code xml : 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
    <?xml version="1.0" encoding="UTF-8"?>
    <Characteristics entity="tower" levels="7">
    	<level n="0">
    		<maxHp>50</maxHp>
    		<attackRate>5</attackRate>
    		<upgrade>0</upgrade>
    	</level>
    	<level n="1">
    		<maxHp>5</maxHp>
    		<attackRate>0</attackRate>
    		<upgrade>1</upgrade>
    	</level>
    	<level n="2">
    		<maxHp>5</maxHp>
    		<attackRate>0</attackRate>
    		<upgrade>1</upgrade>
    	</level>
    	<!-- ETC... -->
    </Characteristics>

    Exemple avec MaxHp:
    - Niveau 0 la template de base donne la valeur 5 à MaxHp mais cette valeur est écrasée par la template propre à l'entité :
    - Niveau 0 (de base; upgrade=0) : MaxHp (objetDynamique de la template propre à l'entité) = 50
    - Niveau 1 (incrémentiel car upgrade=1) : MaxHp = 50 + 5 (opération d'addition)
    - Niveau 2 (pareil) : MaxHp = 55 + 5 (addition encore)

    Je ne sais pas si c'est ce que t'appelles "Ou additionner / soustraire des valeurs ?" mais en tout cas l'opération faite sur les valeurs dépend du XML de la template de base (<upgradeOperator> et <downgradeOperator>). Exemple: MaxHp de type unsigned int utilise l'opération + et - pour l'upgrade et le downgrade respectivement.
    A noter que chaque valeur dans un level d'une template dérivée sera contenue dans un objetDynamique contenus dans un vector/map (1 par niveau) qui est aussi dans un vector/map (contenant tous les niveaux) et qui appartient à une entité. De cette manière chaque entité à sa template propre.
    Code CPP : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Template propre à une entité:
    vector< map < objetDynamique > > templateEntite;
    // et donc l'accès se ferait avec:
    templateEntite.at(1)["MaxHp"].value() // MaxHp du niveau 1 sauf erreur

    Par contre, tu ne m'as pas dis si mon code sur les classes objetDynamique* est correcte ? Et si c'est bien dans celles-ci que la valeur est stockée (d'après ton avant-dernier post on dirait que oui)

  11. #11
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Ok, je comprends mieux.

    <maxHp> et <attackRate> sont deux caracteristiques differentes ?

    à quoi sert le <upgrade> ? Y'a d'autres valeurs possibles ?

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut
    Oui, elles sont différentes, HpMax défini les hp max de l'entitée et l'autre c'est l'intervalle de temps d'attente minimum entre deux attaques de l'entité.

    Pour Upgrade c'est une caractéristique qui ne concerne pas l'entité directement mais ça définit si le niveau est complet ou incrémentiel (pour l'instant il n'y a que le niveau 0 qui est complet et les suivants sont incrémentiels).

    Oui, il y a d'autres caracteristique comme moveRate, canMove qui sont un unsigned int et un bool respectivement. Il y en a pleins d'autres et la liste va s'allonger. C'est pour cette raison que je veux mettre tout ca dans des fichiers XML et non statiquement, dans le code en dur.

    EDIT: Est-ce que tu pourrais répondre aux questions stp ? Est-ce que le code des classes ressemblent à ce que tu pensais ? Et finalement pour l'aditionner (MaxHp niveau 0 et MaxHp 1 contenus dans des objetDynamiqueInt par exemple), qu'est-ce qui devrait s'en charger ? De plus j'aimerais savoir comment tu fais pour écrire dans cette variable interne, par affectation operator=() ou à la construction ?

    J'espère que j'ai été assez clair sur le fonctionnement des upgrades et co.

    EDIT2: Je viens de me rendre compte qu'il y aura 28 FICHIERS (7x4) pour gérer tout ça et c'est très long à écrire. Ne peut-on pas simplement passer par des classes avec template T ?

  13. #13
    Membre habitué Avatar de b Oo
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 179
    Points : 185
    Points
    185
    Par défaut
    Bonsoir,
    je rejoins l'avis de cynique.
    Lorsque tu fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    if (currType == string("unsigned int")) {
        unsigned int val;
        current->GetValue(&val);
        currCharact = new Characteristic(currName, val);
    }
    Tu testes le type de ta variable et tu récupères la valeur dans le bon type. Puis tu crées une nouvelle caractéristique qui attend un boost::any et donc tu perds le type de ta variable.
    Donc tu pourrais créer des charactéristiques pour des chaque type ou créer une classe template :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    template <typename T>
    class Characteristic
    { 
    public:
      Characteristic(string _charactName, const T & val);
    ...
    private:
      T val;
      ...
    };
    b Oo

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut
    Citation Envoyé par b Oo Voir le message
    Donc tu pourrais créer des charactéristiques pour des chaque type ou créer une classe template
    Je n'avais pas compris que c'était cette solution que disait cynique désolé alors. Je pensais aussi à une classe de ce genre mais il se pose un problème, comment stocker dans un même vector des Characteristic de différentes templates ? Car il me semble qu'il faille spécifier à la création du vector le type de variable ainsi que son template si nécessaire. Ça risque de poser problème puisque le type est varié justement. Comment résoudre ce problème ?

    Deuxièmement, pour générer une variable du type donné en string comme me l'a suggéré buzzkaido me semble non-faisable avec les templates puisqu'il faut spécifier le type. Par contre si on peut prendre le string pour obtenir le type et pouvoir l'utiliser comme pour spécifier le type d'une variable, ça réglerai le problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    string currTypeStr = "unsigned int";
    Type currType = convertStringToType(currTypeStr);
    <currType> value;
    getAttribute("MaxHp", &value);
    Bon je rêve un peu mais cette notion de type contenu et utilisable depuis une variable serait pas mal pratique. Si c'est impossible alors je traiterai cas par cas les différents types au chargement du XML, et le reste sera régler si on peut résoudre le problème du vector ci-dessus.

  15. #15
    Membre habitué Avatar de b Oo
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 179
    Points : 185
    Points
    185
    Par défaut
    Bonsoir,
    Citation Envoyé par jamesb
    Je pensais aussi à une classe de ce genre mais il se pose un problème, comment stocker dans un même vector des Characteristic de différentes templates ?
    Tu pourrais en faisant dériver la classe Characteristic d'une autre non template mais tu perdrais à nouveau le type.
    Tu peux créer un conteneur pour chaque type (string, int, ...) mais cela peut être plus embêtant à gerer.

    Pour le deuxième point, oui cela est n'est pas possible car tu ne connais pas le type.
    b Oo

  16. #16
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut
    Citation Envoyé par b Oo Voir le message
    Tu peux créer un conteneur pour chaque type (string, int, ...) mais cela peut être plus embêtant à gerer.
    Je vais devoir être obliger d'utiliser cette méthode, si quelqu'un a une idée lumineuse je prendrais volontier !

  17. #17
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Pourquoi ne pas vouloir de variant ? (boost::variant)
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  18. #18
    Membre habitué Avatar de b Oo
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 179
    Points : 185
    Points
    185
    Par défaut
    Bonjour,
    sinon plutôt que d'avoir une liste de caractéristiques tu peux faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Characteristic
    {
    public:
      Characteristic & operator+=(const Characteristic & chararacteristic);
      const unsigned int maxHp() const;
    ...
    private:
      unsigned int maxHp;
      unsigned int moveRate;
    ...
    };
    Bon par contre la classe va devenir vraiment compliquée à gérer car il me semble que tu as pas mal de charactéristiques mais tu peux en créer plusieurs. Par exemple, WeaponCharacteristic, PlayerCharacteristic, ...
    Je pense aussi que faire de cette manière rendra le code plus clair. Tu pourras aussi supprimer les types unsigned int, int, ... de ton xml et plutôt faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <?xml version="1.0" encoding="UTF-8"?>
    <EntityPlayerCharacteristic>
    	<maxHp upgradeOperator="+" downgradeOperator="-">5</maxHp>
    	<moveRate upgradeOperator="-" downgradeOperator="+">0</moveRate>
    </EntityPlayerCharacteristic>
    ...
    Tu pourrais même supprimer les opérateurs du xml, car c'est toi qui implémente les opérateurs. Mais si tu fais cela veut dire que tu ne pourras pas avoir une autre entité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    <?xml version="1.0" encoding="UTF-8"?>
    <EntityPlayerCharacteristic>
    	<maxHp upgradeOperator="-" downgradeOperator="+">5</maxHp>
    	<moveRate upgradeOperator="+" downgradeOperator="-">0</moveRate>
    </EntityPlayerCharacteristic>
    Même si pour + et - tu pourrais passer une valeur négative.
    b Oo

  19. #19
    Membre régulier
    Profil pro
    Inscrit en
    Août 2004
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Août 2004
    Messages : 152
    Points : 70
    Points
    70
    Par défaut
    Citation Envoyé par Goten Voir le message
    Pourquoi ne pas vouloir de variant ? (boost::variant)
    Il n'y aucun problème contre variant simplement je n'ai aucune idée de comment l'utiliser. Aurais-tu un exemple pour mon problème ?

    Citation Envoyé par b Oo Voir le message
    Bonjour,
    sinon plutôt que d'avoir une liste de caractéristiques tu peux faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Characteristic
    {
    public:
      Characteristic & operator+=(const Characteristic & chararacteristic);
      const unsigned int maxHp() const;
    ...
    private:
      unsigned int maxHp;
      unsigned int moveRate;
    ...
    };
    Bon par contre la classe va devenir vraiment compliquée à gérer car il me semble que tu as pas mal de charactéristiques mais tu peux en créer plusieurs.
    Justement j'ai faisais comme ça avant et au fur à mesure que j'en rajoutais je commençais à saturer. Chaque caractéristique demande, à changer TOUS les fichiers de caractéristiques, de mettre un Getter, un Setter, de rajouter l'implémentation de l'upgrade et downgrade, de modifier le code et tatati et tatata ... très barbant, non je ne souhaite pas revenir en arrière. Je suis persuadé que mon idée est beaucoup mieux (modularité). Je suis navré b Oo, mais ça m'a vraiment ennuyé de devoir tout modifier à chaque fois sinon j'aurais pris cette solution.

    Je pense que je vais faire une map pour chaque type et faire une fonction qui renvoie la caractéristique en spécifiant le type et le nom.
    Peut-être que boost::variant pourrais me sauver ?

    EDIT: Effectivement variant est puissant, ça fait depuis quelques heures que je ré-écris la classe Characteristic avec le chargement du XML ça a l'air pas mal du tout. Il me reste une seule chose peu élégante et qui n'a pas changé. C'est le chargement de la valeur avec "current->GetValue", je crée la variable dans chaque if avec le bon type ensuite je crée l'instance :

    Code CPP : 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
    typedef boost::variant< unsigned int, signed int, bool, string, damageType, entityRunnerFlags, entityTowerFlags, float > charactType;
    // [...]
     
    		// Try to convert to the right type
    		Characteristic *currCharact;
    		charactType sentVar;
    		// -- unsigned int
    		if (currType == string("unsigned int")) {
    			unsigned int val;
    			current->GetValue(&val);
    			sentVar = val;
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		// -- signed int
    		else if (currType == string("signed int")) {
    			signed int val;
    			current->GetValue(&val);
    			sentVar = val;
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		// -- unsigned int - damageType
    		else if (currType == string("damageType")) {
    			unsigned int val;
    			current->GetValue(&val);
    			sentVar = convertAttackTypeFile(val);
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		// -- unsigned int - entityRunnerFlags
    		else if (currType == string("entityRunnerFlags")) {
    			unsigned int val;
    			current->GetValue(&val);
    			sentVar = convertRunnerFlagsTypeFile(val);
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		// -- unsigned int - entityTowerFlags
    		else if (currType == string("entityTowerFlags")) {
    			unsigned int val;
    			current->GetValue(&val);
    			sentVar = convertTowerFlagsTypeFile(val);
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		// -- bool
    		else if (currType == string("bool")) {
    			bool val;
    			current->GetValue(&val);
    			sentVar = val;
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		// -- string
    		else {
    			string val;
    			current->GetValue(&val);
    			sentVar = val;
    			currCharact = new Characteristic(currName, sentVar);
    		}
    		charactList.push_back( *currCharact );
    		delete currCharact;
     
    		// Try to get operators
    		try {
    			string upOper, downOper;
    			upOper = currEl->GetAttribute("upgradeOperator");
    			downOper = currEl->GetAttribute("downgradeOperator");
    			charactList.at(charactList.size() - 1).setUpDownOperators(upOper, downOper);
    		}
    		catch (ticpp::Exception& ex) {
    			cout << "Cannot get "<<child->Value()<<" operators"<<endl;
    			return false;
    		}
    	}
     
    	cout << "Test values :"<<endl;
    	for (unsigned int i = 0; i < charactList.size(); i++) {
    		try {
    			boost::apply_visitor(charactShowOperator(), *charactList.at(i).getValueP());
    		}
    		catch (...) {
    			cout << "Error !"<<endl;
    		}
    	}

    Voici au passage mes visitors :
    Code CPP : 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
    class charactPlusOperator : public boost::static_visitor<void> {
    	public:
    		// handle each types
    		void operator()(unsigned int &x, unsigned int &y) const {
    			x = (x + y);
    		}
    		void operator()(signed int &x, signed int &y) const {
    			x = (x + y);
    		}
    		void operator()(bool &x, bool &y) const {
    			x = (x & y);
    		}
    		void operator()(string &x, string &y) const {
    			x = (x + y);
    		}
    		void operator()(float &x, float &y) const {
    			x = (x + y);
    		}
    		template <typename T> void operator()(T &x, T &y) const {
    			x = y;
    		}
    		template <typename T, typename U> void operator()(T &x, U &y) const {
    			x = x;
    		}
    		template <typename T> void operator()(T &x) const {
    			x = x;
    		}
    };
     
    class charactMinusOperator : public boost::static_visitor<void> {
    	public:
    		// handle each types
    		void operator()(unsigned int &x, unsigned int &y) const {
    			x = (x - y);
    		}
    		void operator()(signed int &x, signed int &y) const {
    			x = (x - y);
    		}
    		void operator()(bool &x, bool &y) const {
    			x = (x & y);
    		}
    		void operator()(string &x, string &y) const {
    			x = y;
    		}
    		void operator()(float &x, float &y) const {
    			x = (x - y);
    		}
    		template <typename T> void operator()(T &x, T &y) const {
    			x = y;
    		}
    		template <typename T, typename U> void operator()(T &x, U &y) const {
    			x = x;
    		}
    		template <typename T> void operator()(T &x) const {
    			x = x;
    		}
    };
     
    class charactShowOperator : public boost::static_visitor<void> {
    	public:
    		// show each types
    		template <typename T> void operator()(T &x) const {
    			cout << typeid(T).name() <<": "<<x<<endl;
    		}
    };

    Comme vous pouvez le voir, j'ai utilisé les visitors des boost::variant qui sont très intéressants au passage car je les utilisent pour faire mes opérations d'upgrade et downgrade ! Très pratique et propre.
    Donc je disais, il reste à régler le problème du GetValue(&...) pas très propre mais optionnel, si il n'y pas de solution simple ça ira tout de même.

  20. #20
    Membre habitué Avatar de b Oo
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 179
    Points : 185
    Points
    185
    Par défaut
    Pour le GetValue tu peux faire :
    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
     
    class ValueCreator // faire un singleton
    {
    public:
      boost::variant<charactType> createValue(const std::string & type);
     
    private:
      std::map<std::string, std::tr1::function<boost::variant<charactType> (??)> valueCreator;
    };
     
    boost::variant<charactType> createUint(?? current)
    {
      unsigned int val;
      current->GetValue(&val);
      return  boost::variant<charactType>(val);
    }
    ...
    ValueCreator::ValueCreator() : valueCreator()
    {
      valueCreator["unsigned int"] = createUInt;
     ...
    }
    ...
    // l'appel
    currCharact = new Characteristic(currName, ValueCreator::Instance().createValue(currType)(current));
    La classe ValueCreator est un singleton et j'ai mis ?? pour le type de current car je ne sais pas à quoi il correspond.
    b Oo

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. VBA, typage dynamique, références
    Par raboliot dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 11/06/2007, 11h24
  2. Réponses: 36
    Dernier message: 09/09/2006, 03h06
  3. id et typage dynamique
    Par Omfraax dans le forum Développement OS X
    Réponses: 5
    Dernier message: 23/08/2006, 19h13
  4. Réponses: 2
    Dernier message: 14/07/2006, 14h24
  5. [Débat] Que pensez-vous des langages à typage dynamique?
    Par Eusebius dans le forum Langages de programmation
    Réponses: 14
    Dernier message: 16/06/2004, 12h12

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