Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: Framework  (Lu 57193 fois)

0 Membres et 1 Invité sur ce sujet

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #90 le: Décembre 11, 2013, 03:40:49 pm »
Sinon, je ne comprends pas pourquoi on devrait éviter d'utiliser des pointeurs sur void*, car, je trouve ceci beaucoup plus pratique que d'utiliser un any.get<Type>() :

#ifndef APPLICATION
#define APPLICATION
#include "stateManager.h"
#include "resourceManager.h"
namespace sfgl {
class Application {
public :
    Application(sf::RenderWindow &window) : window(window) {
        app = this;
    }
    template <typename R> void addResourceManager (ResourceManager<R>& rmi, std::string name);
    template <typename R> ResourceManager<R>& resourceManager(std::string name);
    virtual void load () = 0;
    virtual void render () = 0;
    sf::RenderWindow& getWindow() {
        return window;
    }
    void addClock(sf::Clock clock, std::string name)  {
        std::map<std::string, sf::Clock>::iterator it = clocks.find(name);
        if (it != clocks.end())
            throw Erreur (13, "This clock already exist!", 0);
        clocks.insert(std::make_pair(name, clock));
    }
    sf::Clock getClock(std::string name) {
        std::map<std::string, sf::Clock>::iterator it = clocks.find(name);
        if (it == clocks.end())
            throw Erreur (14, "Clock not found!", 0);
        return it->second;
    }
    StateManager& getStateManager() {
        return stm;
    }
    static Application *app;
private :
    StateManager stm;
    sf::RenderWindow &window;
    std::map<std::string, sf::Clock> clocks;
    std::map<std::string, ResourceManager<void*>&> resourceManagers;
};
template <typename R>
void Application::addResourceManager(ResourceManager<R>& rmi, std::string name) {
    std::map<std::string, ResourceManager<void*>&>::iterator it = resourceManagers.find(name);
    if (it != resourceManagers.end())
        throw Erreur (11, "Resource manager already added!", 0);
    resourceManagers.insert(std::make_pair(name, rmi));
}
template <typename R>
ResourceManager<R>& Application::resourceManager(std::string name) {
    typename std::map<std::string, ResourceManager<void*>&>::iterator it = resourceManagers.find(name);
    if (it == resourceManagers.end())
        throw Erreur (12, "Resource manager not found!", 0);
    return reinterpret_cast<ResourceManager<R>&>(it->second);
}
Application* Application::app = nullptr;
}
#endif // APPLICATION
 

Ce qui me donne au final comme code :

#ifndef MY_APPLI
#define MY_APPLI
#include "SFGL/Core/application.h"
#include <SFML/Graphics.hpp>
#include "SFGL/Graphics/2D/tile.h"
using namespace sfgl;
using namespace sf;
class MyAppli : public Application {
    public :
    MyAppli(sf::RenderWindow &window) : Application (window) {
        TextureManager tm;
        addResourceManager<Texture>(tm, "TextureManager");
    }
    void load() {
        TextureManager &tm =  resourceManager<Texture>("TextureManager");
        tm.load("tilesets/herbe.png", "HERBE");
    }
    void render() {
        TextureManager &tm =  resourceManager<Texture>("TextureManager");
        Tile *t = new Tile(tm.getResourceByAlias("HERBE"), Vec2f(0, 0), Vec2f(100, 50),IntRect(0, 0, 100, 50), 0);
        while (getWindow().isOpen())
        {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (getWindow().pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                getWindow().close();
        }

        // clear the window with black color
        getWindow().clear(sf::Color::Black);

        // draw everything here...
        getWindow().draw(*t);

        // end the current frame
        getWindow().display();
        }
        delete t;
        tm.deleteResourceByAlias("HERBE");
    }
    ~MyAppli() {

    }

};
#endif // MY_APPLI
 

#include <vector>
#include <iostream>
#include <SFML/Graphics.hpp>
#include "myApplication.h"
using namespace sf;
int main () {
    sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
    MyAppli app(window);
    app.load();
    app.render();

}
 

C'est déjà plus court comme main.  :)

Bref, je suis presque arrivé à terme il ne me reste plus que les classes Cursor, Player, Icon et sfgl::renderWindow à implémenter.

Et ensuite je commencer les tutoriels.  :D
Pour les tutoriels je pense que je vais faire un système de génération de monde aléatoire se sera le plus simple.

Le développeur pourra rendre différents states (les states décriront les états du jeux à un moment donné (menus, monde, ressources à charger, options du jeux, etc...)), on pourra basculer d'un state à l'autre via le statemanager de la classe application.

Ceci permettra de revenir à un menu du jeux par exemple sans devoir tout recharger à la main.

Il faudra hériter et redéfinir certains méthode dans les classes states et application car celles-ci sont abstraites :

States :

reload : rechargement des textures du state. (des menus par exemple)
init : recréation des guis des menus par exemple.
draw : affichage du monde en fonction des options de rendu, des menus correspondant à l'état du jeux, etc...
update : mise à jour de l'état des states en fonction du temps.

Application :

load : chargement des ressources de départ.
init : création du monde.
render : rendu du monde.
update : c'est là dedans que le développeur mettra à jour les states et autres objets du framework en fonction des évènements d'entrée.

Voilà je pense que ça ira comme ça au niveau architecture.






« Modifié: Décembre 11, 2013, 03:57:03 pm par Lolilolight »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Messages: 6287
  • Thor Developer
    • Voir le profil
    • Bromeon
Re : Framework
« Réponse #91 le: Décembre 11, 2013, 03:47:48 pm »
Quelques idées:
  • N'utilise pas de singletons ni des variables globales/statiques. Même s'ils paraissent utiles, ils ont beaucoup de problèmes. Dans une architecture plus grande et modulaire, c'est exactement ce que tu ne veux pas
  • Suis le principle RAII. Il ne faut pas utiliser new ou delete sans encapsulation. Et c'est mieux si l'utilisateur n'est pas obligé d'appeler des méthodes comme deleteResourceByAlias() à la fin.
  • Comme Lo-X a déjà dit, le livre utilise les enums parce qu'ils offrent la sûreté de typage au temps de compiler -- si tu utilise un enumerateur qui n'est pas correcte, le compilateur te donne un erreur. Au contraire, avec strings tu as des bugs, et il faut adapter tout les endroits (et il y en aura beaucoup plus que 1, parce que le alias est la seule possibilité pour accèsser les resources dans ton système).
  • Considère les règles de possession (c'est beaucoup plus simple avec RAII).
  • Avoir void* ou boost::any est très souvent un erreur de design. Avec le polymorphisme dynamique, tu n'as pas besoin de différencer les cas.
Tu peux aussi regarder comme j'ai resolu quelques problems avec la gestion de resources en Thor (tutoriel, documentation). C'est un peu plus compliqué que ce que tu veux faire, mais ça peut quand même aider. Par exemple, thor::MultiResourceCache est capable de mémorizer plusieurs types de resources -- sans void* ou boost::any. Le principle s'appelle "type erasure".
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #92 le: Décembre 11, 2013, 04:16:08 pm »
Merci pour ses idées, je pense que je vais :

-Libérer les ressources automatiquement à la fin.
-Utiliser une enum plutôt qu'un string.
-Regarder ton système de type erasure.

Lo-X

  • Hero Member
  • *****
  • Messages: 618
    • Voir le profil
    • My personal website, with CV, portfolio and projects
Re : Framework
« Réponse #93 le: Décembre 11, 2013, 05:09:33 pm »
Si tu ne veux pas utiliser les enums parce que ça force l'utilisateur a en remplir un et de le situer à un endroit précis et imposé dans le code (ce que je peux comprendre), le système des "Key" de Thor est vraiment très très bien (un peu compliqué à implémenter, mais très pratique par la suite)

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #94 le: Décembre 11, 2013, 05:42:33 pm »
Ton code utilise les std::function du c++11 non ?

Si j'ai bien compris tu stockes les fonctions qui chargent la ressource et celles qui récupèrent les ressources dans une classe.

Moi aussi je faisais comme ça avant avec mon éditeur de map :
Je stockais une fonction qui effectuait une action et l'autre qui la déseffectuait. (Histoire de pouvoir faire les actions annuler/rétablir)

Mais j'ai arrêté car il y a un inconvénient :

-Pour chaque fonctions rajoutées, il faut redéfinir un nouveau pointeur de fonction, en plus de redéfinir la fonction. (Dans ton cas il n'y a que la fonction load (et search) donc ça va encore, ça  ne fait qu'un seul pointeur de fonction à redéfinir, mais, pas dans des cas ou l'on utilise pleins de fonctions différentes avec un nombre d'arguments différents comme c'est le cas dans mon éditeur de map.

Hors que avec mon système, il suffit juste de rajouter la fonction dans mon "resource holder".
Plutôt que de devoir passer par 36 000 classes pour avoir au final pleins de fonctions avec le même type de retour et le même nombre d'arguments. (Ca, c'est comme je faisais avant)

-Dans ton cas la ressource se détruit si plus rien ne la référence si j'ai bien compris le système des shared pointeurs. (Donc, lorsque la clé qui contient le shared pointeur est détruite vu que je vois que tu n'utilise aucune collection pour stocker la clé dans ta classe multicache.)

Bref c'est chouette, c'est plus sûr et on ne risque pas d’accéder à une ressource qui n'existe plus mais ce n'est pas vraiment nécessaire avec le principe RAII comme tu dis :
J'ai toujours appris à programmer sans utiliser des shared pointeurs et en gérant la mémoire moi même. (car les shared pointeurs ont plusieurs défaut : on ne peut pas les utiliser dans une classe copiable, notamment, et ni dans un vector, un array, etc...)
De ce fait j'ai toujours opté pour le système qui se rapproche de java : ArrayList maList<?> ou le ? réprésente n'importe quel type tout comme le pointeur sur void du c++.

Mais ça, c'est juste une question de goût.

De plus j'ai vu que tu utilises les shared pointeurs et unique pointeurs du c++11, qui me pose certains problèmes de compatibilité suivants les différents compilateurs. (Bon au départ je pensais utiliser le c++11 mais dès que j'ai eu des problèmes d'incompatibilités entre les versions 64 bits et 32 bits de compitateurs dont certains implémentaient déjà les nouvelles fonctionnalités du c++11 et d'autres pas)

Je me suis dis, autant en revenir à la méthode plus ancienne. (Moi sûr mais qui fonctionne aussi bien que la nouvelle méthode pour autant qu'on la maîtrise)

Pour les identifieurs, je ne suis pas pour non plus, car, je trouve ça chiant de devoir déclarer un identifiant dans le code source pour chaque ressource, je préfère récupérer mes ressources via une variable constante. (une chaine de caractère constante)

Si il se trompe dans le nom de l'alias certes ça plantera à l'exécution, mais au pire y'a la fonction rechercher/remplacer je pense que ça ira plus vite que de devoir déclarer tous pleins de variables de type key ou autre pour récupérer une texture.

Sinon je me demande si en c++ il y a moyen de lancer une exception à la compilation, ça, je trouve ça dommage que les compilateurs ne permettent pas de le faire. :/


Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #95 le: Décembre 11, 2013, 05:59:13 pm »
Et au sujet des variables statique et globales, elles sont pratiques dans certains cas. (pour certains type d'objets qui ne peuvent être qu'unique)

PS : et puis, si c'est pour refaire un système comme thor, autant réutiliser thor. (Je pensais l'utiliser avec SFGL mais, ce n'est pas vraiment mon but au final)
« Modifié: Décembre 11, 2013, 06:06:20 pm par Lolilolight »

Excellium

  • Jr. Member
  • **
  • Messages: 70
    • Voir le profil
Re : Framework
« Réponse #96 le: Décembre 11, 2013, 06:17:36 pm »
Citer
Sinon je me demande si en c++ il y a moyen de lancer une exception à la compilation, ça, je trouve ça dommage que les compilateurs ne permettent pas de le faire. :/

tu peux utiliser assert, non ?
"Everything should be made as simple as possible, but not simpler."

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #97 le: Décembre 11, 2013, 06:41:28 pm »
Ha oui il faut juste que je regarde comment ça fonctionne, mais sinon, oui je pense que je vais utiliser ça! :P

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Messages: 6287
  • Thor Developer
    • Voir le profil
    • Bromeon
Re : Re : Framework
« Réponse #98 le: Décembre 11, 2013, 07:44:52 pm »
Ton code utilise les std::function du c++11 non ?
Oui. Mais il y a std::tr1::function depuis le TR1 (2005).

Pour chaque fonctions rajoutées, il faut redéfinir un nouveau pointeur de fonction, en plus de redéfinir la fonction.
Tu parles de quelles fonctions et pointeurs ? Il faut seulement en définir si les clés (thor::ResourceKey) existantes ne suffisent pas.

-Dans ton cas la ressource se détruit si plus rien ne la référence si j'ai bien compris le système des shared pointeurs.
Il y a deux strategies : L'un est comme tu dis, l'autre garde les ressources jusqu'à ce que tu les enlève (ou le cache et le dernier shared_ptr se détruisent).

Bref c'est chouette, c'est plus sûr et on ne risque pas d’accéder à une ressource qui n'existe plus mais ce n'est pas vraiment nécessaire avec le principe RAII comme tu dis :
J'ai toujours appris à programmer sans utiliser des shared pointeurs et en gérant la mémoire moi même.
Tu as raison, mon système de ressources est plutôt compliqué, pour la plupart de cas un approche plus simple suffit.

(car les shared pointeurs ont plusieurs défaut : on ne peut pas les utiliser dans une classe copiable, notamment, et ni dans un vector, un array, etc...)
Ce n'est pas 100% vrai : Tu peux les utiliser dans des classes copiables et dans des containers, mais il faut savoir que l'objet référencé n'est pas copié.

Si tu veux que le pointeur copie aussi l'objét, tu peux utiliser aurora::CopiedPtr, une autre classe que j'ai développée.

De ce fait j'ai toujours opté pour le système qui se rapproche de java : ArrayList maList<?> ou le ? réprésente n'importe quel type tout comme le pointeur sur void du c++.
void* n'est pas "typage-sûr", tu ne sais pas ce qui se cache derrière. Et tu ne peux rien faire sans savoir. C'est à cause de ça qu'il y a des téchniques très elegantes, comme type erasure.

De plus j'ai vu que tu utilises les shared pointeurs et unique pointeurs du c++11, qui me pose certains problèmes de compatibilité suivants les différents compilateurs.
Il y a std::tr1::shared_ptr qui existe depuis 2005, et std::auto_ptr depuis 1998. Malgré qu'il faut être prudent en utilisant auto_ptr, c'est beaucoup mieux que gérér la mêmoire manuellement. En plus, une petite classe smart-pointeur comme boost::scoped_ptr est écrit dans une quart d'heure.

Si il se trompe dans le nom de l'alias certes ça plantera à l'exécution, mais au pire y'a la fonction rechercher/remplacer je pense que ça ira plus vite que de devoir déclarer tous pleins de variables de type key ou autre pour récupérer une texture.
Ca depend. Récemment j'ai écrit un système de scripte Lua, où tout est géré avec des strings. C'est plus dynamiques, mais j'ai déjà eu pleine des erreurs parce que les strings n'ont pas été correctes, et j'ai dû debugger quelque temps. Si tu as un projet moyen où tu n'as pas besoin de la fléxibilité des identifiers dynamiques, les enums peuvent valoir la peine.

Sinon je me demande si en c++ il y a moyen de lancer une exception à la compilation
static_assert

Mais naturellement, il n'est pas possible de vérifier les strings pendant la compilation, alors ça ne t'aide pas.
« Modifié: Décembre 11, 2013, 07:46:39 pm par Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #99 le: Décembre 11, 2013, 10:08:50 pm »
Citer
Oui. Mais il y a std::tr1::function depuis le TR1 (2005).
Ok
Citer
Tu parles de quelles fonctions et pointeurs ? Il faut seulement en définir si les clés (thor::ResourceKey) existantes ne suffisent pas.

Oui ça suffit pour ton système de ressource, mais pas pour des autres systèmes plus complexes, moi j'ai plusieurs fonctions différentes, avec un association de string à un id, je stocke aussi le path pour le sauver dans un fichier avec des ids. (de type int)
Histoire de ne plus avoir à recharger à chaque fois les textures dès que je lance mon éditeur de map.

Citer
void* n'est pas "typage-sûr", tu ne sais pas ce qui se cache derrière. Et tu ne peux rien faire sans savoir. C'est à cause de ça qu'il y a des téchniques très elegantes, comme type erasure.

Je n'ai jamais entendu parler de type::erasure, il est vrai que derrière le type void* on ne sait pas ce qui se cache derrière et ça peut paraître moins élégant mais je pense que pour les conteneurs ça suffit, je n'ai pas vraiment besoin de savoir que le "resource holder" que je stocke dans mon application est un resource manager de texture.
Et puis pour le réseau par exemple de SFML, Laurent utilise aussi un pointeurs sur void*.

Et chez moi ça fonctionne et ça me suffit donc je ne vais pas trop compliqué les choses. (Je pense que dans mon cas le pointeurs void* suffit vu que ça me sert juste à définir un type pour le stockage de mes ResourceManager dans l'application) :

std::map<std::string, ResourceManager<void*> >
 

Puis, je n'ai qu'à faire une reconversion en ResourceManager<R> lorsque je récupère le resourceManager. (donc je ne touche pas au type, je le convertis juste en void* avec reinterpret_cast pour pouvoir le stocker dans le conteneur)
Avec lequel je récupère ensuite toutes les informations sur les ressources pour stocker tout dans un fichier. (path, id) lors du sauvegarde.
Ton système ne me permet pas de faire ça.



Citer
Ca depend. Récemment j'ai écrit un système de scripte Lua, où tout est géré avec des strings. C'est plus dynamiques, mais j'ai déjà eu pleine des erreurs parce que les strings n'ont pas été correctes, et j'ai dû debugger quelque temps. Si tu as un projet moyen où tu n'as pas besoin de la fléxibilité des identifiers dynamiques, les enums peuvent valoir la peine.

Je ne connais pas les scripts lua, et puis pour se tromper dans un nom d'alias il faut déjà y aller fort, mais, je me débrouillerai pour que ça mette une erreur de compilation.

PS : et puis hum derrière un template tu ne sais pas non plus ce qui se cache derrière, à moins de spécialisé le template comme je l'ai fait là avec R* car ça peut être aussi bien un pointeur, qu'une référence..., et à partir du moment ou tu manipule de la mémoire sur des void* au lieu d'utiliser un template ou un shared_ptr alors là oui en effet ce n'est pas élégant du tout.

Mais ici, ce n'est pas le cas. (Car je n'ai pas utilisé de pointeur sur void dans ma classe ResourceManager, je sais donc que je manipule un pointeur sur un template)

Bref pour moi, nos deux systèmes sont à peu prêt équivalent.

« Modifié: Décembre 11, 2013, 10:28:15 pm par Lolilolight »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : Framework
« Réponse #100 le: Décembre 11, 2013, 10:48:15 pm »
Citer
Je n'ai jamais entendu parler de type::erasure
C'est très exactement ce que fait l'équivalent de boost::any que j'ai posté il y a quelques jours : tu "effaces" (-> erase) le type réel derrière une façade générique. L'avantage c'est qu'en interne, derrière la façade, les informations de typage de l'objet sont conservées, contrairement à un void*. Tu peux donc les exploiter lorsqu'il le faut.

Citer
il est vrai que derrière le type void* on ne sait pas ce qui se cache derrière et ça peut paraître moins élégant mais je pense que pour les conteneurs ça suffit, je n'ai pas vraiment besoin de savoir que le "resource holder" que je stocke dans mon application est un resource manager de texture.
Si tu le stockes, c'est que tu as besoin de t'en reservir plus tard. Si tu t'en sers, c'est forcément en le re-typant (puisque tu ne peux rien faire avec un void*), et en re-typant ton objet il y a un risque d'erreur. Alors que si tu t'es debrouillé pour garder le type d'une quelconque manière, le compilateur va te crier dessus si tu fais un truc incorrect.

Citer
Et puis pour le réseau par exemple de SFML, Laurent utilise aussi un pointeurs sur void*.
Pas pour stocker et réutiliser des objets. Juste pour passer des paramètres de type "adresse mémoire", ce qui est la définition même du type void*.

Citer
Et chez moi ça fonctionne et ça me suffit donc je ne vais pas trop compliqué les choses.
Tu codes pour apprendre non ? Dommage de s'arrêter sur des "ça marche alors ça me va", alors que tu peux t'améliorer sur certains points avec l'aide des gens du forum ;)

Citer
Puis, je n'ai qu'à faire une reconversion en ResourceManager<R> lorsque je récupère le resourceManager
C'est ultra dangereux ce que tu fais. Bien qu'issus du même template, ResourceManager<R> et ResourceManager<U> ne sont pas le même type. Convertir de l'un à l'autre n'est par définition pas correct -- même si en pratique ça marche. Evite ce genre de pratique.

Citer
je le convertis juste en void* avec reinterpret_cast pour pouvoir le stocker dans le conteneur
Pas besoin de reinterpret_cast ici :
- T* vers void* est automatique
- void* vers T* peut être réalisé avec un static_cast

On pinaille, mais un static_cast est plus strict qu'un reinterpret_cast, du coup le compilateur te criera plus volontiers dessus si tu te foires.

Citer
mais, je me débrouillerai pour que ça mette une erreur de compilation.
Comme déjà dit, ce n'est pas possible. Pour avoir des erreurs de compilation sur des identificateurs inconnus, il faut déclarer les identificateurs connus... donc avec un enum ;)

Citer
et puis hum derrière un template tu ne sais pas non plus ce qui se cache derrière
Si, tu le sais. Du moins tu peux le savoir. Ce qui est important c'est que le type réel soit toujours là. Si tu n'en as pas besoin alors tant mieux, si tu en as besoin alors tu peux toujours l'exploiter. C'est ça la force des templates.

Citer
Bref pour moi, nos deux systèmes sont à peu prêt équivalent.
Sauf que l'un est une approche naïve, qui pourra marcher certes, mais qui révèlera ses faiblesses sur le long terme. Mais il faut bien se rendre compte par soi-même pour apprendre, non ? ;)
Laurent Gomila - SFML developer

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #101 le: Décembre 12, 2013, 09:42:06 am »
Citer
C'est très exactement ce que fait l'équivalent de boost::any que j'ai posté il y a quelques jours : tu "effaces" (-> erase) le type réel derrière une façade générique. L'avantage c'est qu'en interne, derrière la façade, les informations de typage de l'objet sont conservées, contrairement à un void*. Tu peux donc les exploiter lorsqu'il le faut.

Ha ok là je comprends mieux donc ce que tu essayes de me dire c'est que ça :

void* nb = 5;
type_info(nb);
 

Ca ne marche pas.

Du coup ce n'est pas bien parce que je ne peux pas vérifier le type de la variable lorsque je la récupère ?

Dans ce cas je n'ai qu'à conserver le type de la variable dans ma classe ResourceManager<R> et le problème est réglé. :) (Tout comme dans le code source que tu m'as montré plus haut)

Citer
Si tu le stockes, c'est que tu as besoin de t'en reservir plus tard. Si tu t'en sers, c'est forcément en le re-typant (puisque tu ne peux rien faire avec un void*), et en re-typant ton objet il y a un risque d'erreur. Alors que si tu t'es debrouillé pour garder le type d'une quelconque manière, le compilateur va te crier dessus si tu fais un truc incorrect.

Mais je pensais vraiment que le cast me renverrait une erreur en compilation dans ce cas là.  (Enfin normalement il est sensé me crier dessus)

Citer
Pas pour stocker et réutiliser des objets. Juste pour passer des paramètres de type "adresse mémoire", ce qui est la définition même du type void*.

Donc void* ne sert que qu'à passer des paramètres de type adresse mémoire, pas à les stocker....

Citer
C'est ultra dangereux ce que tu fais. Bien qu'issus du même template, ResourceManager<R> et ResourceManager<U> ne sont pas le même type. Convertir de l'un à l'autre n'est par définition pas correct -- même si en pratique ça marche. Evite ce genre de pratique.

C'est vrai,  void* et R sont du même type.

Citer
Pas besoin de reinterpret_cast ici :
- T* vers void* est automatique
- void* vers T* peut être réalisé avec un static_cast

On pinaille, mais un static_cast est plus strict qu'un reinterpret_cast, du coup le compilateur te criera plus volontiers dessus si tu te foires.

Ok.

Citer
Sauf que l'un est une approche naïve, qui pourra marcher certes, mais qui révèlera ses faiblesses sur le long terme. Mais il faut bien se rendre compte par soi-même pour apprendre, non ?

Ouais.

Bref je vais conserver le type de mon template dans la classe ResourceManager et faire une vérification de type moi même alors, mais si le compilateur me crie dessus si je me trompe de type pour le template lors du cast bah, alors là, il n'y a pas vraiment de problème ni d'approche naïve puisque de toute façon ça ne compilera pas.

Halala les collections en java qui acceptent n'importe quel type d'objet me manque. (Juste comme ça part hasard il y en aurait pas en c++) ?

Sur ce je vais faire mon générateur aléatoire de map pour continuer mes tutoriels et ça va cartonner grave.  :D
« Modifié: Décembre 12, 2013, 09:46:02 am par Lolilolight »

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #102 le: Décembre 12, 2013, 09:53:56 am »
CQFD :

class MyAppli : public Application {
    public :
    MyAppli(sf::RenderWindow &window) : Application (window) {
        TextureManager tm;
        addResourceManager<Texture>(tm, "TextureManager");
    }
    void load() {
        TextureManager &tm =  resourceManager<Font>("TextureManager");
        tm.load("tilesets/herbe.png", "HERBE");
    }
 

Citer
||=== TestSFGL, Debug ===|
C:\SFGL\include\SFGL\Core\any.h|39|warning: 'sfgl::any' has a field 'sfgl::any::m_holder' whose type uses the anonymous namespace [enabled by default]|
D:\Projets\Projets-c++\TestSFGL\myApplication.h||In member function 'virtual void MyAppli::load()':|
D:\Projets\Projets-c++\TestSFGL\myApplication.h|15|error: invalid initialization of reference of type 'sfgl::TextureManager& {aka sfgl::ResourceManager<sf::Texture>&}' from expression of type 'sfgl::ResourceManager<sf::Font>'|
C:\SFGL\include\SFGL\Core\..\..\..\include\SFGL\Core\resourceManager.h||In instantiation of 'void sfgl::ResourceManager<R>::load(const string&, const string&) [with R = sf::Texture; std::string = std::basic_string<char>]':|
D:\Projets\Projets-c++\TestSFGL\myApplication.h|16|required from here|
C:\SFGL\include\SFGL\Core\..\..\..\include\SFGL\Core\resourceManager.h|65|warning: suggest parentheses around '&&' within '||' [-Wparentheses]|
||=== Build finished: 1 errors, 4 warnings (0 minutes, 4 seconds) ===|
[/code]

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : Framework
« Réponse #103 le: Décembre 12, 2013, 06:11:06 pm »
Citer
Dans ce cas je n'ai qu'à conserver le type de la variable dans ma classe ResourceManager<R> et le problème est réglé.
Oui enfin je parlais de manière plus globale, pas spécialement du std::type_info correspondant au type. On a très rarement besoin de jouer avec std::type_info en réalité. Dans ton cas, avoir un typage plus fort (que du void*) peut aussi vouloir dire passer par des fonctions virtuelles, ou autre chose (j'ai pas regardé ce que tu fais de tes managers, donc je peux pas en dire plus).

Citer
Mais je pensais vraiment que le cast me renverrait une erreur en compilation dans ce cas là.  (Enfin normalement il est sensé me crier dessus)
Comment le compilateur pourrait savoir quel est le type de l'objet qui se cache derrière ton void* ? Pour lui, un void* c'est juste en entier.

Citer
C'est vrai,  void* et R sont du même type.
???

Citer
mais si le compilateur me crie dessus si je me trompe de type pour le template lors du cast bah, alors là, il n'y a pas vraiment de problème
On est d'accord. Mais ce n'est pas le cas ;)

Citer
Donc void* ne sert que qu'à passer des paramètres de type adresse mémoire, pas à les stocker....
On peut aussi stocker des variables de type void*. Le truc important c'est que ça ne doit pas être un moyen de stocker "tout et n'importe quoi" pour s'en reservir plus tard. La "bonne" utilisation de void*, c'est pour définir une adresse mémoire (peu importe ce qui se cache derrière), rien d'autre.

Citer
Halala les collections en java qui acceptent n'importe quel type d'objet me manque. (Juste comme ça part hasard il y en aurait pas en c++) ?
Non il n'y en n'a pas, et c'est tant mieux.

Citer
CQFD
Ca n'a rien à voir. Tu affectes une variable de type ResourceManager<Font> à une variable de type ResourceManager<Texture>, qui sont deux types incompatibles. C'est comme si tu écrivais "std::string str = 5".
Le problème dont on parle, c'est ça :

class MyAppli : public Application {
    public :
    MyAppli(sf::RenderWindow &window) : Application (window) {
        TextureManager tm;
        addResourceManager<Texture>(tm, "ManagerMysterieux");
    }
    void load() {
        FontManager &tm =  resourceManager<Font>("ManagerMysterieux");
        tm.load("arial.ttf");
    }

Personne ne te criera dessus quand tu vas transtyper ton ResourceManager<void*> stocké sous le nom de "ManagerMysterieux" en ResourceManager<Font>, puisque personne ne saura qu'il s'agit en fait d'un ResourceManager<Texture>.
Laurent Gomila - SFML developer

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : Framework
« Réponse #104 le: Décembre 12, 2013, 07:08:34 pm »
Tu n'as pas vu mon code en entier alors parce que :

Il ne peut pas transtyper car si il veut ajouter un ressource manager dans le cache, il est obligé de passer par la fonction template du cache.

Qui est celle-ci :
addResourceManager<Texture>(tm, "TextureManager");
 

Le pointeur sur void* n'est pas dans ma classe ResourceManager, mais bien dans un cache qui se charge de stocker tous les ressources managers dans une map et de les transtyper.

Un peu comme le fait boost::any d'ailleurs, à part que là, ça stocke plusieurs type de ressources comme le multicahe de thor.

map<string, ResourceManager<void*> > resourceManagers;
 

Mais le développeur n'a pas accès au conteneur directement, il est obligé de passer par le cash de l'application qui va lui même se charger de transtyper ça en void* pour que le cache puisse contenir des ressources manager contenant n'importe quel type de ressources. (Texture, son, etc...)

Donc en gros j'ai 2 conteneurs, le "resource manager" qui est comme les classe holder et holder<impl> de ton code et le cache qui correspond à la classe any à part qu'elle peut contenir plusieurs pointeurs sur void* et pas qu'un seul.
Ca ressemble un peu à un boost::variant du coup plutôt qu'à un boost any. (D'ailleurs pour pas te le cacher c'est sur ça que je me suis basé pour créer le resource holder de SFGL)
Mais sinon je suis d'accord avec toi hein sur le fait qu'on ne devrait pas pouvoir accéder directement à un void* si l'on s'en sert dans un conteneur.

D'ailleurs tu vois bien que je n'ai pas fait ça :

map<string, void*> resourceManagers
 

A ne pas confondre avec mon bout de code précédent.
Et je trouve que ça simplifie grandement les choses comparé au système de key de la librairie thor qui est, trop complexe pour moi, les pointeurs sur les fonctions et les shared pointeurs n'est pas quelque chose que je maîtrise vraiment très bien. :/ (Vu que à l'université j'ai appris à m'en passer)

Je pense que je vais essayer de update le code source sur git-hub ce soir (en recréant le repository), ça permettra à certains de mieux comprendre ce que je fais.

De plus j'ai besoin de conserver les paths vers les textures dans mon holder, pour les stocker dans un fichier, ceci m'évite de devoir recréer un conteneur pour chaque application contenant les path vers les ressources que j'utilise.

Donc la seule et unique fonction de thor pour récupérer un pointeur sur une ressource ne me suffit pas. :/

Et ce code me permet d'éviter de devoir passer par divers foncteurs.
C'est ce qui fait d'ailleurs la force des langages compiler contrairement au langage interprêtés (principalement ceux des scripts) ou là, la seconde méthode avec les pointeurs de fonction est à privilégié.

D'ailleurs certains langage comme le javascript permettent de le faire automatiquement tout comme les nouveautés du c++11. (Mais pour moi ceci n'est pas indispensable dans un langage compilé et typé, car, le type erasing comme tu le cites permet de régler le problème)
« Modifié: Décembre 12, 2013, 07:28:25 pm par Lolilolight »

 

anything