30

Bon, ben je suis en train de modifier mon source comme ça :
- un header de fonctions inline pour wrapper les fonctions SDL (mais inline marche-t-il toujours ? à vérifier !) : http://www.mirari.fr/7r3v
- des petites classes pour contenir temporairement des objets ou listes d'objets créés dans les constructeurs (ici, la classe TextureContainer) : http://www.mirari.fr/MgvL

Le containeur initialise son contenu à nullptr (merci C++11), et le libère automatiquement en cas de pépin. Mais si tout a bien marché, il le donne a l'objet réellement intéressé et l'oublie, pour ne pas le libérer par erreur.
Quand j'aurai fait plusieurs conteneurs, je verrai éventuellement comment templater ça, pour le moment je manque de recul.

Et au final, le code du ./2 devient ça : http://www.mirari.fr/liia

On note le gros progrès effectué dans la direction voulue, taille du code et lisibilité, merci encore à tous ! top

31

(En tout cas ce qe je peux dire c'est que czertains bout de code vu dans ce topic avec du wordwrap a moins de 80 colonne c'est super moche)
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

32

Bon, alors est-ce que je peux proposer ma solution, moi ? (Par contre, je ne maîtrise pas super bien la syntaxe du C, et ma solution est totalement pour le fun, hein tongue et elle présuppose que les retours sont soit positifs ou nuls soit négatifs ou nuls)
}[code] m_Texture = SDL_CreateTexture(Game::get()->renderer(), px, SDL_TEXTUREACCESS_TARGET, width, height); int test = 0; test = SDL_SetTextureBlendMode(m_Texture, SDL_BLENDMODE_BLEND)+SDL_SetRenderDrawColor(Game::get()->renderer(), 0, 0, 0, 0)+SDL_RenderClear(Game::get()->renderer())+SDL_SetRenderTarget(Game::get()->renderer(), m_Texture); if (test != 0 || m_Texture == nullptr) { if (m_Texture != nullptr) { cleanup(); } throw Exception(ERROR_SDL_STR);
avatar

33

Ca marche, oui, mais si j'ai d'autres valeurs de retour ça n'ira pas. De plus, tu fais des opérations sur une texture sans savoir si elle existe, donc si ça déconne, le message d'erreur ne sera pas clair.
Au fait, je cherche pas à faire le plus court possible, mais quelque chose de raisonnablement lisible, modifiable et maintenable, en gros cheeky

34

Folco (./33) :
De plus, tu fais des opérations sur une texture sans savoir si elle existe, donc si ça déconne, le message d'erreur ne sera pas clair.
Ah oui, mince, ça m'apprendra à ne pas chercher à comprendre le code triso

Bon, spontanément et plus sérieusement, voilà ma solution, qui correspond à ce qui a déjà été proposé :
m_Texture = SDL_CreateTexture(Game::get()->renderer(), px, SDL_TEXTUREACCESS_TARGET, width, height); if (m_Texture != nullptr) { throw Exception(ERROR_SDL_STR); } if ((SDL_SetTextureBlendMode(m_Texture, SDL_BLENDMODE_BLEND) == 0) || (SDL_SetRenderDrawColor(Game::get()->renderer(), 0, 0, 0, 0) == 0) || (SDL_RenderClear(Game::get()->renderer()) == 0) || (SDL_SetRenderTarget(Game::get()->renderer(), m_Texture) == 0)) { cleanup(); throw Exception(ERROR_SDL_STR); }
avec, éventuellement, cette variante (ça dépend des possibles évolutions de ton code) :
bool err = false; m_Texture = SDL_CreateTexture(Game::get()->renderer(), px, SDL_TEXTUREACCESS_TARGET, width, height); if (m_Texture != nullptr) { if ((SDL_SetTextureBlendMode(m_Texture, SDL_BLENDMODE_BLEND) == 0) || (SDL_SetRenderDrawColor(Game::get()->renderer(), 0, 0, 0, 0) == 0) || (SDL_RenderClear(Game::get()->renderer()) == 0) || (SDL_SetRenderTarget(Game::get()->renderer(), m_Texture) == 0)) { cleanup(); err = true; } } else { err = true; } if (err) { throw Exception(ERROR_SDL_STR); }
avatar

35

Là c'est ok en effet, et la première solution est celle de Zeph en substance. Les grands esprits toussa cheeky
La seconde est pas mal, tellement simple que je n'y avais pas pensé, à ce if. C'est ue bonne option aussi, à voir si c'est plus simple que ce que j'ai proposé, merci beaucoup. smile

36

ou tu peux tout remplacer par un appel a la fonction

void faitMoiCePutainDeTrucBordel(void);
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

37

et tu rajoutes :
system("fallout4.exe");
Et là tu te prends pour un warrior de la programmation de JV tripo

38

Folco (./35) :
Là c'est ok en effet, et la première solution est celle de Zeph en substance. Les grands esprits toussa cheeky
Ouais, la seule grosse différence est que j'accolade et je parenthèse systématiquement ^^
avatar

39

Godzil : lol

Folco : grin


GT Sans erreur !
avatar
Accrochez vous ca va être Cerebral !!

40

(Moi perso je fais un abort dans ces cas là, sauf si c'est une erreur que l'on peut raisonnablement avoir ou si l'application est critique).

41

Que fait l'OS avec un abort, il va libérer aussi bien que moi ? Je sais pas comment fonctionne un OS moderne, par exemple est-ce que les libs utilisées vont être releasées, ainsi que les ressources qu'elles auront créées ?

42

sauf si c'est une erreur que l'on peut raisonnablement avoir ou si l'application est critique).
Ou par exemple si c'est une librairie destinée à être utilisée par d'autres, auquel cas c'est vilain de mettre des appels du genre assert() / abort() / exit(), rattrapables seulement à un niveau très haut par l'utilisateur de la librairie.
J'ai utilisé ZeroMQ du temps où ils abusaient des assert(), c'était chiant de se payer des aborts durs à cause de leur trop large utilisation d'assert(). Même si on avait un watchdog applicatif et qu'on essayait de faire une gestion correcte des erreurs, à partir d'un certain moment.
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

43

Bon, j'ai écrit un template pour essayer de généraliser les choses. Déjà, une heure de potassage pour comprendre la syntaxe bordélique à souhait, sans comprendre pourquoi certaines choses me paraissent redondantes. En fait, j'en ai jamais écrit avant, donc je découvre.
Ca donne ça :
ResourceHolder.hpp
template <typename restype> class ResourceHolder { public: ResourceHolder(restype resource); ~ResourceHolder(); restype resource() const; restype release();; private: restype m_Resource; };
ResourceHOlder.cpp
template<typename restype> ResourceHolder<restype>::ResourceHolder(restype resource) { m_Resource = resource; } template<typename restype> ResourceHolder<restype>::~ResourceHolder() { if (m_Resource != nullptr) delete m_Resource; } template<typename restype> restype ResourceHolder<restype>::resource() const { return m_Resource; } template<typename restype> restype ResourceHolder<restype>::release() { restype res = m_Resource; m_Resource = nullptr; return res; } template<> ResourceHolder<SDL_Surface*>::~ResourceHolder() { if (m_Resource != nullptr) SDL_FreeSurface(m_Resource); } template<> ResourceHolder<SDL_Texture*>::~ResourceHolder() { if (m_Resource != nullptr) SDL_DestroyTexture(m_Resource); }
Voilà, c'est tout simple, quelques one-liners et deux spécialisations pour libérer correctement les SDL_Surface* et SDL_Texture* (merci le raccourci Ctrl-F ! embarrassed)

Evidemment, quand j'en instancie, ça déconne dans les grandes largeur : ResourceHolder<SDL_Surface*> surface(wIMG_Load(filename));
=> ResourceFactory.cpp:41: undefined reference to `ResourceHolder<SDL_Surface*>::ResourceHolder(SDL_Surface*)'

J'explique mon idée : Je veux un ResourceHolder, contenant un type SDL_Surface*. Son nom est surface, et il est construit avec en argument la valeur de retour de wIMG_Load(filename). Voilà, c'est pas compliqué, et pourtant j'ai la furieuse impression que g++ et moi ne sommes pas sur la même longueur d'onde. grin
J'ai parcouru plusieurs tutos et mon bouquin, je vois pas où je pèche, pourriez-vous m'aider svp, parce que je commence à sécher, merci d'avance cheeky



Quand ça marchera, ça permettra de faire des choses bien propres et safes, enfin, à mes yeux grin
Source propre et safe à mes yeux
SDL_Texture* ResourceFactory::makeButtonState(SDL_Renderer* renderer, Uint32 px, SDL_Rect* iconRect, SDL_Texture* iconTexture, int frame, int offsetX, int offsetY) { // Create the texture of the button and put its frame ResourceHolder<SDL_Texture*> button(wSDL_CreateTexture(renderer, px, SDL_TEXTUREACCESS_TARGET, BUTTON_WIDTH, BUTTON_HEIGHT)); wSDL_SetTextureBlendMode(button.resource(), SDL_BLENDMODE_BLEND); ... // Reset render target wSDL_SetRenderTarget(renderer, nullptr); return button.release(); }
smile

44

Réponse en deux parties : pourquoi ça marche pas, et pourquoi à ta place je n'utiliserais pas un template ici :

1 - Ça ne fonctionne pas parce que tu as appliqué ton template sur une classe et non sur une méthode. Du coup si tu veux faire de la spécialisation de template, c'est à dire faire ce que tu fais plus tard en définissant un comportement particulier pour les types SDL_Surface* et SDL_Texture* il faut que tu le fasses pour l'intégralité de la définition de la classe (= toutes ses méthodes, y compris constructeurs et destructeurs). L'explication est que deux spécialisations différentes (par exemple ResourceHolder<A> et ResourceHolder<B>) n'ont aucun code en commun, il s'agit de deux classes indépendantes dans ton code et l'une ne peut pas magiquement utiliser du code de l'autre quand tu oublies de le définir. Après compilation, c'est la même chose que si tu avais déclaré deux classes ResourceHolderA et ResourceHolderB sans aucune relation entre elles.

2 - À ta place j'éviterais d'utiliser un template ici, et même de façon plus générale j'éviterais d'utiliser des templates à moins d'être vraiment certain que c'est une bonne réponse à ton problème. L'une des raisons est que les templates sont très complexes à lire ET à écrire, et avant de nuire autant à la maintenabilité de ton code il faut que tu sois convaincu que ça vaille le coup. Ici ça me semble un peu artificiel : tu as deux types qui ont une utilisation plus ou moins similaire (SDL_Texture et SDL_Surface), du coup tu as très envie de faire une classe générique pour les encapsuler. Sauf qu'elle n'est pas générique (= réutilisable) du tout, puisque finalement tu es obligé de déplacer tout le code intéressant dans les spécialisations et il ne reste quasiment plus rien dans la partie générique (et ça va devenir encore pire quand tu auras corrigé ton problème). Tu peux surement écrire une fonction template qui te rendra l'écriture un peu plus agréable, puis éventuellement une classe générique (mais celle fois réellement, c'est à dire une classe pour laquelle tu peux écrire toute la définition sans avoir à spécifier le type de ses paramètres template) qui l'utilisera.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

45

Merci beaucoup ! Pour ton avis sur les templates, pas de soucis, ça m'a l'air clair. Probablement pour ça que j'ai tourné le dos à la STL.
Zeph (./44) :
l'une ne peut pas magiquement utiliser du code de l'autre quand tu oublies de le définir. Après compilation, c'est la même chose que si tu avais déclaré deux classes ResourceHolderA et ResourceHolderB sans aucune relation entre elles.
En fait, c'était pas impossible pour moi, pour une raison très simple : le compilateur pourrait très bien utiliser les méthodes "générales" du template avec les types utilisés ici et là, et ne remplacer celles qui sont spécialisées que quand elles sont fournies.

Dans ce cas, j'aurais eu deux classes avec leurs méthodes distinctes (une pour les surfaces, une autre pour les textures), mais les destructeurs génériques auraient été zappés et remplacés par les spécialisations.
Comme le compilateur a tous les renseignements dans le template pour générer les autres méthodes, j'ai pensé que ce n'était pas nécessaire de lui préciser.

Mais bon, on est pas à une redondance près avec ces templates, quand je vois quelque chose comme template<typename restype> ResourceHolder<restype>::ResourceHolder(restype resource), je trouve qu'il y a déjà un <restype> en trop.

46

Je ne sais pas si le fonctionnement que tu décris existe. Techniquement je ne vois rien qui l'empêche, mais malheureusement en pratique tu es obligé de tout définir quand tu spécialises ton template. Après oui, la syntaxe des templates est très lourde (parcequ'elle supporte des tonnes de choses dont tu ne te serviras probablement jamais). Pour info dans des langages plus modernes comme par exemple Java ou C# (et probablement pas mal des petits nouveaux genre Rust ou Go mais je ne les connais pas) toute la partie template<typename T> n'existe pas, il suffit de préciser MaClasse<T> ou MaMethode<T> pour que le compilateur en déduise que T est un type paramètre de ta classe/méthode. Ça n'est pas aussi puissant, mais c'est bien plus simple à lire et à écrire et ça convient dans 99% des cas d'utilisation.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

47

Folco (./43) :
Voilà, c'est tout simple, quelques one-liners et deux spécialisations pour libérer correctement les SDL_Surface* et SDL_Texture*
Même si ça fonctionnait (ce qui n'est pas le cas, cf. la suite de la discussion), tu aurais quand-même oublié de surcharger la méthode release explicite.
EDIT: En fait non, j'ai mal compris ce que fait ta méthode release, je lui donnerais un nom moins ambigu, genre give or disown.

De plus, tu as déclaré un template sur un type quelconque, en présupposant qu'il est un pointeur, ce n'est pas très propre. Pourquoi pas:
template <typename restype> class ResourceHolder
{
    public:
        ResourceHolder(restype *resource);
        ~ResourceHolder();
        restype *resource() const;
        restype *release();;

    private:
        restype *m_Resource;
};
?
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

48

À sa place j'essaierai d'utiliser un std::shared_ptr auquel on peut passer un "deleter" custom. D'ailleurs j'ai trouvé quelqu'un qui essaie visiblement de faire exactement la même chose que toi sur stackoverflow, la discussion t'intéressera surement :

http://stackoverflow.com/questions/12340810/using-custom-deleter-with-stdshared-ptr
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

49

Ah tiens c'est marrant, bon je lirai ça demain ça me passionnera sûrement, merci beaucoup !

50

Voilà ce que tu veux (tout est inline, donc mets tout dans le .hpp, pas besoin de .cpp):namespace ResourceHolderImpl { template<typename restype> inline void destroy(restype *res) { if (res) delete res; } template<> inline void destroy<SDL_Surface>(SDL_Surface *res) { if (res) SDL_FreeSurface(res); } template<> inline void destroy<SDL_Texture>(SDL_Texture *res) { if (res) SDL_DestroyTexture(res); } } template <typename restype> class ResourceHolder { public: ResourceHolder(restype *resource) : m_Resource(resource) {} ~ResourceHolder() {ResourceHolderImpl::destroy(m_Resource);} restype *resource() const {return m_Resource;} restype *give() { restype *res = m_Resource; m_Resource = nullptr; return res; } private: restype *m_Resource; };
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

51

Yup, c'était ce que proposait Zeph d'ailleurs, et c'était exactement ce que j'avais en tête quand j'ai voulu faire ça. Et effectivement, "give" est bien meilleur que "release" qui évoque une libération, donc potentiellement un destruction. Merci bien top