Bonjour.
Lors de la destruction de la classe que vous trouverez ci-dessus, je
me retrouve avec une erreur de manipulation de mémoire. VC++7 renvoie:
HEAP[prog.exe]: Heap block at 088B0788 modified at 088B07B5 past
requested size of 25
Une inspection du call stack informe que le pb se produit lors d'un
delete emit par le "scalar destructing constructor".
Une idée?
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");
Bonjour.
Lors de la destruction de la classe que vous trouverez ci-dessus, je
me retrouve avec une erreur de manipulation de mémoire. VC++7 renvoie:
HEAP[prog.exe]: Heap block at 088B0788 modified at 088B07B5 past
requested size of 25
Une inspection du call stack informe que le pb se produit lors d'un
delete emit par le "scalar destructing constructor".
Une idée?
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");
Bonjour.
Lors de la destruction de la classe que vous trouverez ci-dessus, je
me retrouve avec une erreur de manipulation de mémoire. VC++7 renvoie:
HEAP[prog.exe]: Heap block at 088B0788 modified at 088B07B5 past
requested size of 25
Une inspection du call stack informe que le pb se produit lors d'un
delete emit par le "scalar destructing constructor".
Une idée?
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).
Par contre, j'ai remarqué que le pb n'apparaissait que lorsqu'une
exception CBSPLoad (cf plus bas) est déclanchée. Il doit se passer un
truc au niveau des constructeurs de classes dérivées auquel je ne
pense pas..
class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};
class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};
Le tout sous VC++7
using std::string;
handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).
Par contre, j'ai remarqué que le pb n'apparaissait que lorsqu'une
exception CBSPLoad (cf plus bas) est déclanchée. Il doit se passer un
truc au niveau des constructeurs de classes dérivées auquel je ne
pense pas..
class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};
class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};
Le tout sous VC++7
using std::string;
handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).
Par contre, j'ai remarqué que le pb n'apparaissait que lorsqu'une
exception CBSPLoad (cf plus bas) est déclanchée. Il doit se passer un
truc au niveau des constructeurs de classes dérivées auquel je ne
pense pas..
class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};
class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};
Le tout sous VC++7
using std::string;
handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
Je réponds àOups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).
Par contre, j'ai remarqué que le pb n'apparaissait que
lorsqu'une exception CBSPLoad (cf plus bas) est déclanchée.
Il doit se passer un truc au niveau des constructeurs de
classes dérivées auquel je ne pense pas..
class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};
class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};
Le tout sous VC++7
using std::string;
handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.
2) Votre exception est transmise par copie semble-t-il, et non
par adresse.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?
Je réponds à superbabar51@hotmail.com
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).
Par contre, j'ai remarqué que le pb n'apparaissait que
lorsqu'une exception CBSPLoad (cf plus bas) est déclanchée.
Il doit se passer un truc au niveau des constructeurs de
classes dérivées auquel je ne pense pas..
class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};
class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};
Le tout sous VC++7
using std::string;
handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.
2) Votre exception est transmise par copie semble-t-il, et non
par adresse.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?
Je réponds àOups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).
Par contre, j'ai remarqué que le pb n'apparaissait que
lorsqu'une exception CBSPLoad (cf plus bas) est déclanchée.
Il doit se passer un truc au niveau des constructeurs de
classes dérivées auquel je ne pense pas..
class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};
class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};
Le tout sous VC++7
using std::string;
handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.
2) Votre exception est transmise par copie semble-t-il, et non
par adresse.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?
Patrick 'Zener' Brunet wrote:Je réponds à[...] le code
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.
Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas le
choix -- le système d'exploitation le fait pour nous). Alors, on
positionne le new_handler, et on s'occupe d'autres problèmes.
Sinon, il faut certainement des classes faites sur mésure dans
l'exception, pour gerer des chaînes sans allocation dynamique.
2) Votre exception est transmise par copie semble-t-il, et non
par adresse.
Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où l'utilisateur le
construit (comme temporaire) va disparaître.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.
Ça implique *une* copie, c'est tout. Qu'il l'attrappe par copie
implique une deuxième. Rien devant le coût d'une exception en
général.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
C'est le problème du compilateur, non le sien. Le programmeur
n'a pas le droit de detruire l'exception lui-même.
4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?
Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté. Tout
ce qu'il a posté semble correct, mais on ne peut pas exclure une
erreur dans les autres parties.
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?
D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre. Et
un déboggueur y est à peu près inutile.
Ce qu'il lui faut, évidemment, c'est quelque chose comme Purify.
Mais ce n'est pas donné, s'il est étudiant ou programmeur
amateur. (S'il est programmeur professionnel, évidemment, sa
boîte ferait bien de l'acheter, malgré le prix. Parce qu'elle
regagnera largement le prix en temps de programmeur gagné.)
Patrick 'Zener' Brunet wrote:
Je réponds à superbabar51@hotmail.com
[...] le code
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.
Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas le
choix -- le système d'exploitation le fait pour nous). Alors, on
positionne le new_handler, et on s'occupe d'autres problèmes.
Sinon, il faut certainement des classes faites sur mésure dans
l'exception, pour gerer des chaînes sans allocation dynamique.
2) Votre exception est transmise par copie semble-t-il, et non
par adresse.
Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où l'utilisateur le
construit (comme temporaire) va disparaître.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.
Ça implique *une* copie, c'est tout. Qu'il l'attrappe par copie
implique une deuxième. Rien devant le coût d'une exception en
général.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
C'est le problème du compilateur, non le sien. Le programmeur
n'a pas le droit de detruire l'exception lui-même.
4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?
Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté. Tout
ce qu'il a posté semble correct, mais on ne peut pas exclure une
erreur dans les autres parties.
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?
D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre. Et
un déboggueur y est à peu près inutile.
Ce qu'il lui faut, évidemment, c'est quelque chose comme Purify.
Mais ce n'est pas donné, s'il est étudiant ou programmeur
amateur. (S'il est programmeur professionnel, évidemment, sa
boîte ferait bien de l'acheter, malgré le prix. Parce qu'elle
regagnera largement le prix en temps de programmeur gagné.)
Patrick 'Zener' Brunet wrote:Je réponds à[...] le code
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.
Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas le
choix -- le système d'exploitation le fait pour nous). Alors, on
positionne le new_handler, et on s'occupe d'autres problèmes.
Sinon, il faut certainement des classes faites sur mésure dans
l'exception, pour gerer des chaînes sans allocation dynamique.
2) Votre exception est transmise par copie semble-t-il, et non
par adresse.
Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où l'utilisateur le
construit (comme temporaire) va disparaître.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.
Ça implique *une* copie, c'est tout. Qu'il l'attrappe par copie
implique une deuxième. Rien devant le coût d'une exception en
général.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
C'est le problème du compilateur, non le sien. Le programmeur
n'a pas le droit de detruire l'exception lui-même.
4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?
Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté. Tout
ce qu'il a posté semble correct, mais on ne peut pas exclure une
erreur dans les autres parties.
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?
D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre. Et
un déboggueur y est à peu près inutile.
Ce qu'il lui faut, évidemment, c'est quelque chose comme Purify.
Mais ce n'est pas donné, s'il est étudiant ou programmeur
amateur. (S'il est programmeur professionnel, évidemment, sa
boîte ferait bien de l'acheter, malgré le prix. Parce qu'elle
regagnera largement le prix en temps de programmeur gagné.)
Patrick 'Zener' Brunet wrote:Je réponds à[...] le code
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions :
vous jouez avec le feu. Il serait plus sage, autant que
possible, de disposer d'une exception (voire d'un petit
pool) qui soit (entièrement) allouées à l'avance, de
manière à ne pas introduire d'éventualité d'échec.
Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas
le choix -- le système d'exploitation le fait pour nous).
Alors, on positionne le new_handler, et on s'occupe d'autres
problèmes.
Sinon, il faut certainement des classes faites sur mésure
dans l'exception, pour gerer des chaînes sans allocation
dynamique.
Il m'est arrivé de développer de telles classes : elles sont
construites comme des objets globaux, sans allocation de
mémoire dynamique, et donc elles ne sont plus jamais
construites ni détruites : seulement réquisitionnées puis
restituées (elles fournissent une méthode static Throw() ).
Pour ce qui est de la chaîne message, en fait j'avais défini
un protocole permettant de stocker dans le corps de
l'exception les éléments variants (2 entiers et un tableau de
127 + 1 caractères), la classe Exception offrant une fonction
type sprintf() contrôlée pour utiliser ça.
Ca permet donc de justifier ma réponse à la suite...
Sinon pour ce qui est de l'abort(), étant plutôt orienté
informatique-industrielle-sans-renoncement-à-la-mémoire-dynamique,
évidemment c'est hors de question (et donc il faudra que je me
remette un peu au courant des évolutions de la gestion
"optimiste" de mémoire sous Linux depuis 2 ans).
2) Votre exception est transmise par copie semble-t-il, et
non par adresse.
Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où
l'utilisateur le construit (comme temporaire) va
disparaître.
Avec ma convention précédente, justement, c'était un pointeur
vers l'exception qui était transmis, lui donc par copie en
effet.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une
part très inefficace, et d'autre part remultiplie les cas
d'échec.
Ça implique *une* copie, c'est tout. Qu'il l'attrappe par
copie implique une deuxième. Rien devant le coût d'une
exception en général.
Et s'il utilise throw; dans un bloc catch() pour la relancer ?
Je me souviens-là des tests que j'ai pu faire lors de la mise
au point d'une classe genre "string", au niveau de l'opérateur
d'affectation et du constructeur de copie (et en prenant en
compte l'instance anonyme transitoire lorsqu'on retourne un
objet entier plutôt qu'un pointeur). Donc, selon comment cette
instance d'exception est construite, remplie puis "lancée",
j'hésite sur le nombre possible de copies.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
C'est le problème du compilateur, non le sien. Le
programmeur n'a pas le droit de detruire l'exception
lui-même.
Donc toujours avec la même convention, le compilo fait ce
qu'il veut du pointeur qui est transmis, mais si un bloc
catch() ne relance pas l'exception, elle doit s'arrêter là.
Dans mes classes, il y avait donc à ce niveau un
pE->Release(), comme ça se fait dans les classes d'exceptions
des MFC par exemple (quoiqu'on puisse en dire). Dès lors que
l'exception n'est pas atomique et nécessite une destruction
(ou restitution dans mon cas) "évoluée", finalement je
préfère.4) Pour ce qui est de sa création correcte, ça n'apparaît
pas dans le code soumis... Comment écrivez-vous les
constructeurs, et l'instruction throw ?
Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté.
Tout ce qu'il a posté semble correct, mais on ne peut pas
exclure une erreur dans les autres parties.
Oui, je voudrais voir le throw...5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code
obtenez-vous l'erreur ?
D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre.
Et un déboggueur y est à peu près inutile.
Je n'ai pas pu déterminer ce qui était en train d'être
détruit, ni à quel moment. Ca manque...
Ce qu'il lui faut, évidemment, c'est quelque chose comme
Purify. Mais ce n'est pas donné, s'il est étudiant ou
programmeur amateur. (S'il est programmeur professionnel,
évidemment, sa boîte ferait bien de l'acheter, malgré le
prix. Parce qu'elle regagnera largement le prix en temps de
programmeur gagné.)
Bienheureux les concepteurs qui ont réussi à recruter un
employeur sérieux !
Moi je suis à nouveau en train de chercher, et j'espère que je
ne vais pas à nouveau devoir rafistoler les moutons crevés
laissés par le type d'avant, la conception des produits
d'avenir de la boîte étant dans le même temps confiée à des
bricolos pour raisons de budget :-@
Heureusement qu'on peut encore faire de la R&D sérieuse durant
ses loisirs :-D
Patrick 'Zener' Brunet wrote:
Je réponds à superbabar51@hotmail.com
[...] le code
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions :
vous jouez avec le feu. Il serait plus sage, autant que
possible, de disposer d'une exception (voire d'un petit
pool) qui soit (entièrement) allouées à l'avance, de
manière à ne pas introduire d'éventualité d'échec.
Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas
le choix -- le système d'exploitation le fait pour nous).
Alors, on positionne le new_handler, et on s'occupe d'autres
problèmes.
Sinon, il faut certainement des classes faites sur mésure
dans l'exception, pour gerer des chaînes sans allocation
dynamique.
Il m'est arrivé de développer de telles classes : elles sont
construites comme des objets globaux, sans allocation de
mémoire dynamique, et donc elles ne sont plus jamais
construites ni détruites : seulement réquisitionnées puis
restituées (elles fournissent une méthode static Throw() ).
Pour ce qui est de la chaîne message, en fait j'avais défini
un protocole permettant de stocker dans le corps de
l'exception les éléments variants (2 entiers et un tableau de
127 + 1 caractères), la classe Exception offrant une fonction
type sprintf() contrôlée pour utiliser ça.
Ca permet donc de justifier ma réponse à la suite...
Sinon pour ce qui est de l'abort(), étant plutôt orienté
informatique-industrielle-sans-renoncement-à-la-mémoire-dynamique,
évidemment c'est hors de question (et donc il faudra que je me
remette un peu au courant des évolutions de la gestion
"optimiste" de mémoire sous Linux depuis 2 ans).
2) Votre exception est transmise par copie semble-t-il, et
non par adresse.
Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où
l'utilisateur le construit (comme temporaire) va
disparaître.
Avec ma convention précédente, justement, c'était un pointeur
vers l'exception qui était transmis, lui donc par copie en
effet.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une
part très inefficace, et d'autre part remultiplie les cas
d'échec.
Ça implique *une* copie, c'est tout. Qu'il l'attrappe par
copie implique une deuxième. Rien devant le coût d'une
exception en général.
Et s'il utilise throw; dans un bloc catch() pour la relancer ?
Je me souviens-là des tests que j'ai pu faire lors de la mise
au point d'une classe genre "string", au niveau de l'opérateur
d'affectation et du constructeur de copie (et en prenant en
compte l'instance anonyme transitoire lorsqu'on retourne un
objet entier plutôt qu'un pointeur). Donc, selon comment cette
instance d'exception est construite, remplie puis "lancée",
j'hésite sur le nombre possible de copies.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
C'est le problème du compilateur, non le sien. Le
programmeur n'a pas le droit de detruire l'exception
lui-même.
Donc toujours avec la même convention, le compilo fait ce
qu'il veut du pointeur qui est transmis, mais si un bloc
catch() ne relance pas l'exception, elle doit s'arrêter là.
Dans mes classes, il y avait donc à ce niveau un
pE->Release(), comme ça se fait dans les classes d'exceptions
des MFC par exemple (quoiqu'on puisse en dire). Dès lors que
l'exception n'est pas atomique et nécessite une destruction
(ou restitution dans mon cas) "évoluée", finalement je
préfère.
4) Pour ce qui est de sa création correcte, ça n'apparaît
pas dans le code soumis... Comment écrivez-vous les
constructeurs, et l'instruction throw ?
Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté.
Tout ce qu'il a posté semble correct, mais on ne peut pas
exclure une erreur dans les autres parties.
Oui, je voudrais voir le throw...
5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code
obtenez-vous l'erreur ?
D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre.
Et un déboggueur y est à peu près inutile.
Je n'ai pas pu déterminer ce qui était en train d'être
détruit, ni à quel moment. Ca manque...
Ce qu'il lui faut, évidemment, c'est quelque chose comme
Purify. Mais ce n'est pas donné, s'il est étudiant ou
programmeur amateur. (S'il est programmeur professionnel,
évidemment, sa boîte ferait bien de l'acheter, malgré le
prix. Parce qu'elle regagnera largement le prix en temps de
programmeur gagné.)
Bienheureux les concepteurs qui ont réussi à recruter un
employeur sérieux !
Moi je suis à nouveau en train de chercher, et j'espère que je
ne vais pas à nouveau devoir rafistoler les moutons crevés
laissés par le type d'avant, la conception des produits
d'avenir de la boîte étant dans le même temps confiée à des
bricolos pour raisons de budget :-@
Heureusement qu'on peut encore faire de la R&D sérieuse durant
ses loisirs :-D
Patrick 'Zener' Brunet wrote:Je réponds à[...] le code
AMHA il y a quelques gros problèmes de conception là-dedans :
1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions :
vous jouez avec le feu. Il serait plus sage, autant que
possible, de disposer d'une exception (voire d'un petit
pool) qui soit (entièrement) allouées à l'avance, de
manière à ne pas introduire d'éventualité d'échec.
Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas
le choix -- le système d'exploitation le fait pour nous).
Alors, on positionne le new_handler, et on s'occupe d'autres
problèmes.
Sinon, il faut certainement des classes faites sur mésure
dans l'exception, pour gerer des chaînes sans allocation
dynamique.
Il m'est arrivé de développer de telles classes : elles sont
construites comme des objets globaux, sans allocation de
mémoire dynamique, et donc elles ne sont plus jamais
construites ni détruites : seulement réquisitionnées puis
restituées (elles fournissent une méthode static Throw() ).
Pour ce qui est de la chaîne message, en fait j'avais défini
un protocole permettant de stocker dans le corps de
l'exception les éléments variants (2 entiers et un tableau de
127 + 1 caractères), la classe Exception offrant une fonction
type sprintf() contrôlée pour utiliser ça.
Ca permet donc de justifier ma réponse à la suite...
Sinon pour ce qui est de l'abort(), étant plutôt orienté
informatique-industrielle-sans-renoncement-à-la-mémoire-dynamique,
évidemment c'est hors de question (et donc il faudra que je me
remette un peu au courant des évolutions de la gestion
"optimiste" de mémoire sous Linux depuis 2 ans).
2) Votre exception est transmise par copie semble-t-il, et
non par adresse.
Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où
l'utilisateur le construit (comme temporaire) va
disparaître.
Avec ma convention précédente, justement, c'était un pointeur
vers l'exception qui était transmis, lui donc par copie en
effet.
Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une
part très inefficace, et d'autre part remultiplie les cas
d'échec.
Ça implique *une* copie, c'est tout. Qu'il l'attrappe par
copie implique une deuxième. Rien devant le coût d'une
exception en général.
Et s'il utilise throw; dans un bloc catch() pour la relancer ?
Je me souviens-là des tests que j'ai pu faire lors de la mise
au point d'une classe genre "string", au niveau de l'opérateur
d'affectation et du constructeur de copie (et en prenant en
compte l'instance anonyme transitoire lorsqu'on retourne un
objet entier plutôt qu'un pointeur). Donc, selon comment cette
instance d'exception est construite, remplie puis "lancée",
j'hésite sur le nombre possible de copies.
3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.
C'est le problème du compilateur, non le sien. Le
programmeur n'a pas le droit de detruire l'exception
lui-même.
Donc toujours avec la même convention, le compilo fait ce
qu'il veut du pointeur qui est transmis, mais si un bloc
catch() ne relance pas l'exception, elle doit s'arrêter là.
Dans mes classes, il y avait donc à ce niveau un
pE->Release(), comme ça se fait dans les classes d'exceptions
des MFC par exemple (quoiqu'on puisse en dire). Dès lors que
l'exception n'est pas atomique et nécessite une destruction
(ou restitution dans mon cas) "évoluée", finalement je
préfère.4) Pour ce qui est de sa création correcte, ça n'apparaît
pas dans le code soumis... Comment écrivez-vous les
constructeurs, et l'instruction throw ?
Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté.
Tout ce qu'il a posté semble correct, mais on ne peut pas
exclure une erreur dans les autres parties.
Oui, je voudrais voir le throw...5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code
obtenez-vous l'erreur ?
D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre.
Et un déboggueur y est à peu près inutile.
Je n'ai pas pu déterminer ce qui était en train d'être
détruit, ni à quel moment. Ca manque...
Ce qu'il lui faut, évidemment, c'est quelque chose comme
Purify. Mais ce n'est pas donné, s'il est étudiant ou
programmeur amateur. (S'il est programmeur professionnel,
évidemment, sa boîte ferait bien de l'acheter, malgré le
prix. Parce qu'elle regagnera largement le prix en temps de
programmeur gagné.)
Bienheureux les concepteurs qui ont réussi à recruter un
employeur sérieux !
Moi je suis à nouveau en train de chercher, et j'espère que je
ne vais pas à nouveau devoir rafistoler les moutons crevés
laissés par le type d'avant, la conception des produits
d'avenir de la boîte étant dans le même temps confiée à des
bricolos pour raisons de budget :-@
Heureusement qu'on peut encore faire de la R&D sérieuse durant
ses loisirs :-D
CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count!, char *
__formal=0x00000000) Line 34 + 0x9 C++
::_Copy(unsigned int _Newsize , unsigned int _Oldlen=0) Line 1458 +
0xf C++
::_Grow(unsigned int _Newsize , bool _Trimúlse) Line 1485 C++
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,
CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count=21, char *
__formal=0x00000000) Line 34 + 0x9 C++
::_Copy(unsigned int _Newsize=20, unsigned int _Oldlen=0) Line 1458 +
0xf C++
::_Grow(unsigned int _Newsize=20, bool _Trim=false) Line 1485 C++
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,
CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count!, char *
__formal=0x00000000) Line 34 + 0x9 C++
::_Copy(unsigned int _Newsize , unsigned int _Oldlen=0) Line 1458 +
0xf C++
::_Grow(unsigned int _Newsize , bool _Trimúlse) Line 1485 C++
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,
En effet, l'exception est maintenant déclanchée par le
constructeur et non plus par le destructeur alors que je n'ai
pas changé le code (moi rien comprendre). Le pb problème doit
donc être plus général. Va falloir revoir l'"architecture" du
programme. (au fait, étudiant et pas programmeur... Mais deux
ans sans codé et on oublie vite la théorie et les bases:
analyser avant programmer)
Pour ceux que ça interesse.. (j'aimerai bien comprendre comme
même en fait ;-))
CBSP::CBSP(string sFileName)
{
/* ... */
if(sFileName[0] != '' && sFileName[1] != ':')
{
// then sFileName probably does NOT contain a full path to the file
m_sFileName = Config::GameDir;
m_sFileName = m_sFileName + "Maps";
/************ Exception déclanchée par cette ligne */
m_sFileName = m_sFileName + sFileName;
} else
m_sFileName = sFileName;
}
Voici le "Call Stack":
CrazyMarket.exe!strlen() Line 78 Asm
CrazyMarket.exe!exception::exception(const exception & that={...})
+
0x31 C++
CrazyMarket.exe!std::bad_alloc::bad_alloc(const std::bad_alloc &
__that={...}) + 0x13 C++
CrazyMarket.exe!std::_Nomemory() Line 9 + 0xd C++
CrazyMarket.exe!operator new(unsigned int size!) Line 15 C++CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count!, char *
__formal=0x00000000) Line 34 + 0x9 C++
CrazyMarket.exe!std::allocator<char>::allocate(unsigned int
_Count!) Line 138 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Copy(unsigned int _Newsize , unsigned int _Oldlen=0) Line 1458
+
0xf C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Grow(unsigned int _Newsize , bool _Trimúlse) Line 1485 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}, unsigned int _Roff=0, unsigned int _CountB94967295)
Line 599 + 0x10 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}) Line 586 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}) Line 468 C++
CrazyMarket.exe!CBSP::CBSP(std::basic_string<char,std::char_traits<char>,s td::allocator<char>
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,
HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00141f07,
int nCmdShow=1) Line 29 + 0x4f C++
CrazyMarket.exe!WinMainCRTStartup() Line 251 + 0x30 C
kernel32.dll!7c816d4f()
ntdll.dll!7c925b4f()
kernel32.dll!7c8399f3()
exception nomemory? alors que le stack contient seulement
d'une string (et en plus, les chaines en elle même sont en
mémoire dynamique, corrigez si je me trompe)
En effet, l'exception est maintenant déclanchée par le
constructeur et non plus par le destructeur alors que je n'ai
pas changé le code (moi rien comprendre). Le pb problème doit
donc être plus général. Va falloir revoir l'"architecture" du
programme. (au fait, étudiant et pas programmeur... Mais deux
ans sans codé et on oublie vite la théorie et les bases:
analyser avant programmer)
Pour ceux que ça interesse.. (j'aimerai bien comprendre comme
même en fait ;-))
CBSP::CBSP(string sFileName)
{
/* ... */
if(sFileName[0] != '\' && sFileName[1] != ':')
{
// then sFileName probably does NOT contain a full path to the file
m_sFileName = Config::GameDir;
m_sFileName = m_sFileName + "\Maps\";
/************ Exception déclanchée par cette ligne */
m_sFileName = m_sFileName + sFileName;
} else
m_sFileName = sFileName;
}
Voici le "Call Stack":
CrazyMarket.exe!strlen() Line 78 Asm
CrazyMarket.exe!exception::exception(const exception & that={...})
+
0x31 C++
CrazyMarket.exe!std::bad_alloc::bad_alloc(const std::bad_alloc &
__that={...}) + 0x13 C++
CrazyMarket.exe!std::_Nomemory() Line 9 + 0xd C++
CrazyMarket.exe!operator new(unsigned int size=21) Line 15 C++
CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count=21, char *
__formal=0x00000000) Line 34 + 0x9 C++
CrazyMarket.exe!std::allocator<char>::allocate(unsigned int
_Count=21) Line 138 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Copy(unsigned int _Newsize=20, unsigned int _Oldlen=0) Line 1458
+
0xf C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Grow(unsigned int _Newsize=20, bool _Trim=false) Line 1485 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}, unsigned int _Roff=0, unsigned int _Count=4294967295)
Line 599 + 0x10 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}) Line 586 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}) Line 468 C++
CrazyMarket.exe!CBSP::CBSP(std::basic_string<char,std::char_traits<char>,s td::allocator<char>
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,
HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00141f07,
int nCmdShow=1) Line 29 + 0x4f C++
CrazyMarket.exe!WinMainCRTStartup() Line 251 + 0x30 C
kernel32.dll!7c816d4f()
ntdll.dll!7c925b4f()
kernel32.dll!7c8399f3()
exception nomemory? alors que le stack contient seulement
d'une string (et en plus, les chaines en elle même sont en
mémoire dynamique, corrigez si je me trompe)
En effet, l'exception est maintenant déclanchée par le
constructeur et non plus par le destructeur alors que je n'ai
pas changé le code (moi rien comprendre). Le pb problème doit
donc être plus général. Va falloir revoir l'"architecture" du
programme. (au fait, étudiant et pas programmeur... Mais deux
ans sans codé et on oublie vite la théorie et les bases:
analyser avant programmer)
Pour ceux que ça interesse.. (j'aimerai bien comprendre comme
même en fait ;-))
CBSP::CBSP(string sFileName)
{
/* ... */
if(sFileName[0] != '' && sFileName[1] != ':')
{
// then sFileName probably does NOT contain a full path to the file
m_sFileName = Config::GameDir;
m_sFileName = m_sFileName + "Maps";
/************ Exception déclanchée par cette ligne */
m_sFileName = m_sFileName + sFileName;
} else
m_sFileName = sFileName;
}
Voici le "Call Stack":
CrazyMarket.exe!strlen() Line 78 Asm
CrazyMarket.exe!exception::exception(const exception & that={...})
+
0x31 C++
CrazyMarket.exe!std::bad_alloc::bad_alloc(const std::bad_alloc &
__that={...}) + 0x13 C++
CrazyMarket.exe!std::_Nomemory() Line 9 + 0xd C++
CrazyMarket.exe!operator new(unsigned int size!) Line 15 C++CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count!, char *
__formal=0x00000000) Line 34 + 0x9 C++
CrazyMarket.exe!std::allocator<char>::allocate(unsigned int
_Count!) Line 138 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Copy(unsigned int _Newsize , unsigned int _Oldlen=0) Line 1458
+
0xf C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Grow(unsigned int _Newsize , bool _Trimúlse) Line 1485 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}, unsigned int _Roff=0, unsigned int _CountB94967295)
Line 599 + 0x10 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}) Line 586 C++
CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >
&
_Right={...}) Line 468 C++
CrazyMarket.exe!CBSP::CBSP(std::basic_string<char,std::char_traits<char>,s td::allocator<char>
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,
HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00141f07,
int nCmdShow=1) Line 29 + 0x4f C++
CrazyMarket.exe!WinMainCRTStartup() Line 251 + 0x30 C
kernel32.dll!7c816d4f()
ntdll.dll!7c925b4f()
kernel32.dll!7c8399f3()
exception nomemory? alors que le stack contient seulement
d'une string (et en plus, les chaines en elle même sont en
mémoire dynamique, corrigez si je me trompe)