OVH Cloud OVH Cloud

Suppression d'instances inutiles ?

53 réponses
Avatar
Mickael Pointier
[Je viens de réinstaller mon PC suite à un changement d'OS, pourriez vous me
signaler le moindre problème dans ce message, que ca soit au niveau de
l'encodage ou que sais-je encore ? Merci d'avance.]

Je viens de passer de Visual 6 à VS.net 2003, et en convertissant mes
quelques projets C++, j'ai constaté un certain nombre de différences au
niveau du comportement de ces deux compilateurs. VS.net est visiblement bien
plus rigoureux et clair dans les erreurs qu'il signale, par contre il m'a
fait un truc que je n'ai pas trop aimé.

Ce que j'aimerai savoir si c'est lui qui a raison (la norme dit qu'il faut
le faire) et que donc je me reposait sur un comportement indéfini, ou bien
si ce que je faisait était pas mauvais en soit et que c'est donc VS.net qui
abuse.

Le point en question, c'est que dans ma librairie de déboggage, j'ai un
certain nombre de petites classes utilitaires qui n'ont de code que dans le
constructeur et dans le destructeur.

Certaines me servent à trouver des fuites de mémoire, d'autre à me sauver la
pile des appels dans un log, etc... tout ca ne sert qu'en debug, en release
le code en question est supprimé.

Vu que c'était pratique, j'ai débordé de l'usage purement debug, et j'ai
commencé à m'en servir, comme par exemple avec ma microclasse de
manipulation de répertoires:

==============
class tbDirectoryChanger
{
public:
tbDirectoryChanger(); //!< Simply store the current path
tbDirectoryChanger(const string &new_path); //!< Store the current path,
and set the new one
~tbDirectoryChanger(); //!< Restore the path stored during
construction
private:
void store_current_path(); //!< Utility function, store the current
path
string m_memo_path; //!< Contains the stored path
};
==============

C'est pas forcément très beau, mais ca me permet dans un outil de build de
ressources qui gère beaucoup de chemins d'accès de faire des trucs dans le
genre:

...
{
tbDirectoryChanger tbDC("mon nouveau chemin de travail temporaire");
DoSomething(bla sur le nouveau chemin);
}
...

ce qui fait qu'à la sortie du scope, le directory est automatiquement
restauré.

Avec VC6 ca marchait bien, VC7 lui considère que la variable "tbDC" n'est
pas utilisée, et donc il vire l'instanciation de ma variable, ce qui fait
que mon répertoire n'est pas changé, et mon "DoSomething" il fait n'importe
quoi vu que le répertoire n'est pas validé.

Donc il a le droit de faire ca ???

La variable n'est pas utilisée, soit, mais le constructeur et le destructeur
ne sont pas triviaux.

Vala, merci de m'éclairer là dessus.

Mike

10 réponses

1 2 3 4 5
Avatar
Mickael Pointier
Alexandre wrote:
============= >> class tbDirectoryChanger
{
public:
tbDirectoryChanger(); //!< Simply store the current path
tbDirectoryChanger(const string &new_path); //!< Store the
current path, and set the new one
~tbDirectoryChanger(); //!< Restore the path stored
during construction
private:
void store_current_path(); //!< Utility function, store the
current path
string m_memo_path; //!< Contains the stored path
};
============= >
on a pas le code des méthodes. Je fais donc un essai avec un corps

simplissime (mais non vide).


Oui, ca suffit.

[...]
Mon compilo C++ (borland C++ builder) passe tout sans pb (aucun
warning) la variable est bien construite puis détruite.
ça serait étrange que visual studio renferme un bug aussi gros. Un
peu plus de détails sur cette classe et DoSomething nous aiderait...


Bon, j'ai trouvé ce qui se passe.
Moi bettement je pensait que ces deux écritures étaient équivalentes:

tbDirectoryChanger tbdc;

et

tbDirectoryChanger tbdc();

Visiblement ca n'est pas le cas, car si je compile avec .net (Version
7.1.3088 en anglais) en debug (je n'ai pas essayé en release) j'ai un
comportement différent selon l'écriture utilisée.

En clair, si j'écrit ca:

tbDirectoryChanger tbDC;

l'objet est correctement créé et détruit, le chemin mémorisé et restauré.

Si j'écrit ca:

tbDirectoryChanger tbDC("Un chemin quelconque");

l'objet est correctement créé avec le chemin bien modifié puis l'ancien
restauré à la destruction.

Par contre si j'écrit ca:

tbDirectoryChanger tbDC();

j'ai le droit à un message:

warning C4930: 'tbDirectoryChanger tbDC(void)': prototyped function not
called (was a variable definition intended?)

En quoi cette écriture est-elle fausse ? Ca ne revient pas à dire "utilise
le constructeur non paramétré" ?
Si quelqu'un pouvait m'éclairer sur le problème, ca serait cool :)

Mike


Avatar
Pierre Maurette
typa:

Pierre Maurette wrote in message
news:...

J'avais remarqué que VC++7 avait l'optimisation virile et joyeuse.
Normal ou pas normal, je n'en sais pas assez en C++ et sur le
DoSomething pour me prononcer.
A tout hasard, essayez:
volatile tbDirectoryChanger tbDC("repert");


On programme à coups d'hazard, maintenant. Si le programme ne marche
pas, on modifie quelque chose de façon aléatoire, en espérant que par je
ne sais pas quelle miracle, il va se mettre à marcher.
Cool, Raoul.

<OP>
Avec VC6 ca marchait bien, VC7 lui considère que la variable "tbDC"
n'est pas utilisée, et donc il vire l'instanciation de ma variable, ce
qui fait que mon répertoire n'est pas changé, et mon "DoSomething" il
fait n'importe quoi vu que le répertoire n'est pas validé.
</OP>
Mode affirmatif, donc je suppose que l'OP a pris les moyens de
vérifier que sa classe n'avait pas été instanciée. J'ai de gros
doutes, je ne vois pas le compilateur zapper du code de constructeur
et destructeur. Mais par politesse (eh oui) je mets sur le même plan
mes doutes sur VC++ et sur son DoSomething. C'est une figure de
réthorique qui permet de ne pas passer pour un goujat.
Je connais un peu le comportement (radical) de VC++7.1 face à
volatile. Saisir 9 caractères pour lever un doute, ce n'est pas cher.
De plus, j'avoue que le C++ me réserve encore bien des surprises (et
pour longtemps encore:-() et je voulais vérifier. Votre message
précédent m'aurait totallement rassuré, mais il est arrivé un peu
tard.
Dans un second message que vous semblez ignorer, je signale que j'ai
testé sur le compilateur utilisé par l'OP et que la classe s'instancie
normalement : c'est LA réponse à LA question posée :
<OP>
Donc il a le droit de faire ca ???
</OP>
Il ne fait pas ça (VC++ .NET 2003). J'en profite pour donner une
méthode parmi d'autre pour lever tout doute sur le contenu de la
"release". La tentation est grande de se contenter de se mettre en
configuration "debug" et de tracer.

J'espère ne jamais avoir à utiliser un programme que tu as écrit.
Dieu m'en préserve également, ce serait un grand honneur pour moi,

mais je craindrais des effets de bord. Je le regrette.

Pierre


Avatar
Mickael Pointier
Le constructeur non trivial et inconnu dans l'unite de compilation a
le droit d'avoir des effets de bords. Le compilateur doit donc
construire tbDC imperativement. La duree de vie de tbDC va jusqu'a
la fin du bloc courant donc au-dela de DoSomething. Je pencherais
pour un bug mais ca me parait enorme comme bug (difficile de croire
qu'il n'ait pas ete detecte par les ecrivains du compilateur).
Peut-etre faudrait-il voir le code du constructeur et de
DoSomething pour en avoir le coeur net?



Il faudrait bien en effet savoir exactement le contexte. Parce que je
ne peux pas croire que le compilateur ôte systèmatiquement de telles
variables. Comme j'ai écris par ailleurs, ça fait partie d'un idiome
consacré (RAII), décrit dans prèsque tous les textes C++.


Depuis mon autre message où j'ai indiqué que le problème ne se produisait
que si j'utilisais la forme avec parenthèse, j'ai voulu voir si le
compilateur générait un code différent dans le cas où j'utilise new:

Que je fasses ca:
tbDirectoryChanger *ptr_tbdc=new tbDirectoryChanger;

ou ca:
tbDirectoryChanger *ptr_tbdc=new tbDirectoryChanger();

J'obtient ca:

00428DF4 push 1Ch
00428DF6 call operator new (43FAA0h)
00428DFB add esp,4
00428DFE mov dword ptr [ebp-0CDCh],eax
00428E04 mov byte ptr [ebp-4],5
00428E08 cmp dword ptr [ebp-0CDCh],0
00428E0F je Tool_Main+144h (428E24h)
00428E11 mov ecx,dword ptr [ebp-0CDCh]
00428E17 call tbDirectoryChanger::tbDirectoryChanger (445320h)
00428E1C mov dword ptr [ebp-0D68h],eax
00428E22 jmp Tool_Main+14Eh (428E2Eh)
00428E24 mov dword ptr [ebp-0D68h],0
00428E2E mov eax,dword ptr [ebp-0D68h]
00428E34 mov dword ptr [ebp-0CD8h],eax
00428E3A mov byte ptr [ebp-4],3
00428E3E mov ecx,dword ptr [ebp-0CD8h]
00428E44 mov dword ptr [ptr_tbdc],ecx

La différence de comportement n'est donc que dans l'instantiation simple.


S'il a un mechanisme de trace (fortement conseillé pour tout sauf les
programmes les plus petits), il n'a que l'activer. S'il n'en a pas, il
pourrait penser d'y en ajouter un. Et en attendant, des sorties vers
std::cerr permet d'avancer -- est-ce que le constructeur est réelement
appelé ? Sinon, est-ce qu'on passe par le code où la variable est
déclarée ; si oui, est-ce que l'essai de changer le répertoire a
réussi@? (A priori, s'il y a eu un échec lors de l'essai de changer le
répertoire, une erreur aurait dû être logguée. Sauf qu'il me semble
que Michael a dit une fois qu'il travaille sur des jeux. Un domain
que je ne connais pas, mais où les règles de programmation son bien
différentes que ce que je vois d'habitude. C'est donc peut-être
normal ou acceptable qu'il n'a pas loggué l'erreur -- qu'est-ce qu'on
ferait avec un log d'un programme de jeu ?)


Dans un outil, et tout est bien loggé, c'est comme ca que je me suis rendu
compte du problème.
L'outil exécute des scripts, et chaque fois que je fais une grosse
modification je lance mes scripts de test, et là ca à foiré.

Mais bon, y'a log et log.

Il me semble normal de détecter qu'une opération de changement de répertoire
échoue.
Par contre détecter le fait que le compilateur à décidé de ne pas appeller
le code en question, heu, je sais pas faire, si ce n'est en le déduisant du
fait que le code derrière foire lamentablement.

Mike



Avatar
Twxs
Régis Troadec wrote:
"Mickael Pointier" a écrit dans le message de
news:c5lob5$stc$

Salut,

[snip le reste]



genre:

...
{
tbDirectoryChanger tbDC("mon nouveau chemin de travail temporaire");
DoSomething(bla sur le nouveau chemin);
}
...

ce qui fait qu'à la sortie du scope, le directory est automatiquement
restauré.




Ca me parait tout a fait normal, à qui ou à quoi s'applique ta fonction
DoSomething ? Est-ce une fonction membre de tbDirectoryChanger, est-ce une
fonction statique ? Je ne le vois pas.


tu n'as pas tout saisi, le DoSomething n'a rien a voire avec la classe
tbDirectoryChanger, elle indique juste une chose a faire dans le nouveau
repertoire courant


sinon, j'utilise frequement ce genre de technique et n'ai aucun probleme
sous tous les VC (6, 7.0 et 7.1). Bienqu'il me semble qu'une option de
compilation retire le code non utilisé, verifie qu'elle ne soit pas
activée.

Twxs


Avatar
Pierre Maurette
typa:

Pierre Maurette wrote in message
[...]

il génère:
12 -> mémoire
mémoire -> registre
registre -> mémoire
mémoire -> registre
registre -> mémoire


Ce qui me semble tout à fait normal.
Mais pas à g++ 3.2 (mingw, sous Windows). Il se contente de:

12 -> mémoire


Sur une machine moderne, je m'attendrais aussi à ce que le compilateur
ajoute des barrières d'écriture et de lecture, de façon à s'assurer que
les l'écritures soient bien visibles de l'exterieur, et que les lectures
va réelement chercher au delà de la cache, mais la plupart des
compilateurs ne le font pas.

D'après tes commentaires, j'ai l'impression que vous ne connaissez pas
Je vois que vous hésitez entre le tu et le vous. Je vous autorise l'un

et l'autre.
la signification de volatile. Ce qui n'est pas grave en soi -- un
programmeur d'applications n'en a jamais besoin. Mais dans ce cas-là, il
ne faut pas s'en servir n'en plus. Ni en conseiller l'utilisation à
d'autres.
L'amabilité est une seconde nature chez vous.

Je vais plus facilement vers le hard et l'assembleur que vers le C++
(grosses lacunes en C++, je me soigne), et le volatile est ma passion.
Je suis l'ornithologue de l'informatique, le fou de la plume et du
croupion. Je fais beaucoup de tests et je trouve qu'utiliser volatile
pour forcer à la génération de code est facile. Il faut simplement se
méfier des compilateurs qui le gèrent mal.
Il y a trop de sources qui présentent "à moitié" volatile, en se
contentant de parler de modifications possibles en dehors de l'emprise
du programme. Les normes citent ces cas, mais comme un exemple. Je me
demande s'il ne serait pas plus simple d'écrire que toute opération du
code source concernant un objet volatile doit être réellement codée,
dans le meilleur respect possible de la chronologie du source.

[...]
Il faudrait bien en effet savoir exactement le contexte. Parce que je ne
peux pas croire que le compilateur ôte systèmatiquement de telles
variables. Comme j'ai écris par ailleurs, ça fait partie d'un idiome
consacré (RAII), décrit dans prèsque tous les textes C++.
Dont acte. Je ne pouvais croire le contraire.


[...]
Pour Mickael, vérifier en ajoutant l'option /FAs à la compilation, et
en regardant le fichier .asm (rechercher sur les mots du source, c'est
à la fin d'un gros fichier).


Et quoi encore ?
J'aime bien voir le code généré, c'est mon vice, avec les volatiles.

Chacun ses danseuses. Le fichier .asm (ou le debogueur machine) est
une bonne solution, même s'il en existe d'autre, qui garantit la
conformité du code observé et du code définitif.

S'il a un mechanisme de trace (fortement conseillé pour tout sauf les
programmes les plus petits), il n'a que l'activer. S'il n'en a pas, il
pourrait penser d'y en ajouter un. Et en attendant, des sorties vers
std::cerr permet d'avancer -- est-ce que le constructeur est réelement
appelé ? Sinon, est-ce qu'on passe par le code où la variable est
déclarée ; si oui, est-ce que l'essai de changer le répertoire a
réussi@? (A priori, s'il y a eu un échec lors de l'essai de changer le
répertoire, une erreur aurait dû être logguée. Sauf qu'il me semble que
Michael a dit une fois qu'il travaille sur des jeux. Un domain que je ne
connais pas, mais où les règles de programmation son bien différentes
que ce que je vois d'habitude. C'est donc peut-être normal ou acceptable
qu'il n'a pas loggué l'erreur -- qu'est-ce qu'on ferait avec un log d'un
programme de jeu ?)
Générer un .asm, alors ?


Pierre


Avatar
Mickael Pointier
[Repost, apparement le premier n'est pas passé]

Alexandre wrote:
============= >> class tbDirectoryChanger
{
public:
tbDirectoryChanger(); //!< Simply store the current path
tbDirectoryChanger(const string &new_path); //!< Store the
current path, and set the new one
~tbDirectoryChanger(); //!< Restore the path stored
during construction
private:
void store_current_path(); //!< Utility function, store the
current path
string m_memo_path; //!< Contains the stored path
};
============= >
on a pas le code des méthodes. Je fais donc un essai avec un corps

simplissime (mais non vide).


Oui, ca suffit.

[...]
Mon compilo C++ (borland C++ builder) passe tout sans pb (aucun
warning) la variable est bien construite puis détruite.
ça serait étrange que visual studio renferme un bug aussi gros. Un
peu plus de détails sur cette classe et DoSomething nous aiderait...


Bon, j'ai trouvé ce qui se passe.
Moi bettement je pensait que ces deux écritures étaient équivalentes:

tbDirectoryChanger tbdc;

et

tbDirectoryChanger tbdc();

Visiblement ca n'est pas le cas, car si je compile avec .net (Version
7.1.3088 en anglais) en debug (je n'ai pas essayé en release) j'ai un
comportement différent selon l'écriture utilisée.

En clair, si j'écrit ca:

tbDirectoryChanger tbDC;

l'objet est correctement créé et détruit, le chemin mémorisé et restauré.

Si j'écrit ca:

tbDirectoryChanger tbDC("Un chemin quelconque");

l'objet est correctement créé avec le chemin bien modifié puis l'ancien
restauré à la destruction.

Par contre si j'écrit ca:

tbDirectoryChanger tbDC();

j'ai le droit à un message:

warning C4930: 'tbDirectoryChanger tbDC(void)': prototyped function not
called (was a variable definition intended?)

En quoi cette écriture est-elle fausse ? Ca ne revient pas à dire "utilise
le constructeur non paramétré" ?
Si quelqu'un pouvait m'éclairer sur le problème, ca serait cool

Mike


begin 666 smile.gif
M1TE&.#EA#P`/`)$!`````+^_O___`````"'Y! $```$`+ `````/`````(N
MC V9QY$"X6(@6GGJO0!)+3RA$XDA:&Y6JGXMIX$K%G,8^2EE]G:4?&ID%+Y#
#`0`[
`
end


Avatar
Jean-Marc Bourguet
"Mickael Pointier" writes:

En clair, si j'écrit ca:

tbDirectoryChanger tbDC;

l'objet est correctement créé et détruit, le chemin mémorisé et restauré.

Si j'écrit ca:

tbDirectoryChanger tbDC("Un chemin quelconque");

l'objet est correctement créé avec le chemin bien modifié puis l'ancien
restauré à la destruction.

Par contre si j'écrit ca:

tbDirectoryChanger tbDC();

j'ai le droit à un message:

warning C4930: 'tbDirectoryChanger tbDC(void)': prototyped function not
called (was a variable definition intended?)

En quoi cette écriture est-elle fausse ? Ca ne revient pas à dire
"utilise le constructeur non paramétré" ? Si quelqu'un pouvait
m'éclairer sur le problème, ca serait cool


La forme qui ne fonctionne pas est une declaration de fonction nommee
tbDC sans parametre et retournant un tbDirectoryChanger. Le warning
t'indique que tu declares cette fonction sans jamais l'appeler.

Il faut se souvenir de deux choses:
- quand qqch est interpretable comme declaration de fonction et
definition, c'est la declaration qui prime;
- on peut declarer des fonctions localement.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Mickael Pointier
warning C4930: 'tbDirectoryChanger tbDC(void)': prototyped function
not called (was a variable definition intended?)

En quoi cette écriture est-elle fausse ? Ca ne revient pas à dire
"utilise le constructeur non paramétré" ? Si quelqu'un pouvait
m'éclairer sur le problème, ca serait cool


La forme qui ne fonctionne pas est une declaration de fonction nommee
tbDC sans parametre et retournant un tbDirectoryChanger. Le warning
t'indique que tu declares cette fonction sans jamais l'appeler.


Donc en fait c'est Visual 6 qui ne fait pas les choses correctement.


Il faut se souvenir de deux choses:
- quand qqch est interpretable comme declaration de fonction et
definition, c'est la declaration qui prime;
- on peut declarer des fonctions localement.


Hu ? Je ne savais pas ca.
Quel est l'intérêt de déclarer une fonction locale ???

Merci en tout cas pour l'explication.

Mike


Avatar
Jean-Marc Bourguet
"Mickael Pointier" writes:

warning C4930: 'tbDirectoryChanger tbDC(void)': prototyped function
not called (was a variable definition intended?)

En quoi cette écriture est-elle fausse ? Ca ne revient pas à dire
"utilise le constructeur non paramétré" ? Si quelqu'un pouvait
m'éclairer sur le problème, ca serait cool


La forme qui ne fonctionne pas est une declaration de fonction nommee
tbDC sans parametre et retournant un tbDirectoryChanger. Le warning
t'indique que tu declares cette fonction sans jamais l'appeler.


Donc en fait c'est Visual 6 qui ne fait pas les choses correctement.


Si pour VC6

foo f();

est une definition de variable et pas une declaration de fonction,
oui.


Il faut se souvenir de deux choses:
- quand qqch est interpretable comme declaration de fonction et
definition, c'est la declaration qui prime;
- on peut declarer des fonctions localement.


Hu ? Je ne savais pas ca.
Quel est l'intérêt de déclarer une fonction locale ???


Ce n'est plus un style tres courant.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
Fabien LE LEZ
On Fri, 16 Apr 2004 14:14:53 +0200, "Mickael Pointier"
wrote:

Quel est l'intérêt de déclarer une fonction locale ?


Il est bien faible, effectivement. Mais si dans le code suivant :

int f(); // 1

void g()
{
int h(); // 2
}

la ligne 1 déclarait une fonction et la ligne 2 déclarait une
variable, ça serait encore plus compliqué...

--
;-)
FLL, Epagneul Breton

1 2 3 4 5