Je savais bien que l'on pouvait faire de l'exit récursif
Voici un exemple de programme qui fait dans la récursion imprévue :
class Ex {
public:
Ex(int a) : val(a) {}
virtual ~Ex() {
printme();
exit(1);
}
virtual void printme() {
std::cout << "Ex(" << val << ")" << std::endl;
}
int val;
};
Ex *e1 = new Ex(1);
Ex *e2 = new Ex(2);
Ex *e3 = new Ex(3);
#define DEL_PTR(p) if (p) { delete p; p=0; }
extern "C" void onexit(void) {
DEL_PTR(e1);
DEL_PTR(e2);
DEL_PTR(e3);
}
int main () {
atexit(onexit);
{
Ex e0(0);
std::cout << "attention à l'exit ! " << std::endl;
}
std::cout << "exit(0)" << std::endl;
exit(0);
return 0;
}
En fait, il suffit de faire ceci - plus besoin de classes ou de
destructeurs -
Ma page de man pour atexit donne ceci :
These functions must not call exit(); if it should be necessary to termi
nate the process while in such a function, the _exit(2) function
should be used. (Alternatively, the function may cause abnormal
process termi nation, for example by calling abort(3).)
Bernard
PS : je suis sous MacOS X, donc d'autres BSD, voire Linux ou Windows
devraient avoir un comportement identique.
Il y a quelques jours dans le thread exceptions multiples, André Heinen écrivait :
Si tu n'as pas de récursion infinie, ça fonctionne sans problème, mais sans appeler les destructeurs des objets automatiques.
Quant à la récursion infinie, je sais que c'est possible, mais je n'en ai jamais vu. Je n'ai encore jamais vu de destructeur qui appelle exit(). Et quand bien même ce serait le cas, encore faudrait-il que ce soient justement des instances de cette classe-là qui soient détruites par exit(). Et j'avais répondu que j'étais déjà tombé sur ce phénomène. J'ai donc
fini par retrouver un vieux bout de source qui le provoque. Avec du copier coller et en simplifiant, cela donne la première partie de code. La deuxième partie est la simplification complète du phénomène.
Certes, le DEL_PTR n'est pas judicieux dans l'exemple. Le extern "C", c'est bien ce que j'avais dans mon code. Je pense qu'à l'époque cela m'était indispensable, mais je ne me souviens plus pourquoi.
Je voulais démontrer que l'utilisation d'exit pouvait être délicate. Lors de la discussion précédente, j'avais oublié que c'était l'utilisation conjointe de exit et de atexit qui était explosive.
Je ne sais plus exactement de quand date ce code - tous les fichiers sont au 01/01/1970 00:00 - mais il a plus de dix ans et c'était mes tous premiers débuts en C++, avec Tubo C++ de Borland, sous MS/DOS. J'ai donc appri ce jour là que l'utilisation d'exit dans les destructeurs était dangereuse.
drkm wrote:
bernard tatin writes:
#define DEL_PTR(p) if (p) { delete p; p=0; }
Le test est inutile.
extern "C" void onexit(void) { DEL_PTR(e1);
Obfuscation.
DEL_PTR(e2); DEL_PTR(e3); } int main () { atexit(onexit);
Pourquoi déclarer un handler de atexit « extern "C" » ?
Il y a quelques jours dans le thread exceptions multiples,
André Heinen écrivait :
Si tu n'as pas de récursion infinie, ça fonctionne sans problème,
mais sans appeler les destructeurs des objets automatiques.
Quant à la récursion infinie, je sais que c'est possible, mais je
n'en ai jamais vu. Je n'ai encore jamais vu de destructeur qui
appelle exit(). Et quand bien même ce serait le cas, encore
faudrait-il que ce soient justement des instances de cette
classe-là qui soient détruites par exit().
Et j'avais répondu que j'étais déjà tombé sur ce phénomène. J'ai donc
fini par retrouver un vieux bout de source qui le provoque. Avec du
copier coller et en simplifiant, cela donne la première partie de code.
La deuxième partie est la simplification complète du phénomène.
Certes, le DEL_PTR n'est pas judicieux dans l'exemple. Le extern "C",
c'est bien ce que j'avais dans mon code. Je pense qu'à l'époque cela
m'était indispensable, mais je ne me souviens plus pourquoi.
Je voulais démontrer que l'utilisation d'exit pouvait être délicate.
Lors de la discussion précédente, j'avais oublié que c'était
l'utilisation conjointe de exit et de atexit qui était explosive.
Je ne sais plus exactement de quand date ce code - tous les fichiers
sont au 01/01/1970 00:00 - mais il a plus de dix ans et c'était mes tous
premiers débuts en C++, avec Tubo C++ de Borland, sous MS/DOS. J'ai donc
appri ce jour là que l'utilisation d'exit dans les destructeurs était
dangereuse.
drkm wrote:
bernard tatin <bernard.tatin@nospam.tele2.fr> writes:
#define DEL_PTR(p) if (p) { delete p; p=0; }
Le test est inutile.
extern "C" void onexit(void) {
DEL_PTR(e1);
Obfuscation.
DEL_PTR(e2);
DEL_PTR(e3);
}
int main () {
atexit(onexit);
Pourquoi déclarer un handler de atexit « extern "C" » ?
Il y a quelques jours dans le thread exceptions multiples, André Heinen écrivait :
Si tu n'as pas de récursion infinie, ça fonctionne sans problème, mais sans appeler les destructeurs des objets automatiques.
Quant à la récursion infinie, je sais que c'est possible, mais je n'en ai jamais vu. Je n'ai encore jamais vu de destructeur qui appelle exit(). Et quand bien même ce serait le cas, encore faudrait-il que ce soient justement des instances de cette classe-là qui soient détruites par exit(). Et j'avais répondu que j'étais déjà tombé sur ce phénomène. J'ai donc
fini par retrouver un vieux bout de source qui le provoque. Avec du copier coller et en simplifiant, cela donne la première partie de code. La deuxième partie est la simplification complète du phénomène.
Certes, le DEL_PTR n'est pas judicieux dans l'exemple. Le extern "C", c'est bien ce que j'avais dans mon code. Je pense qu'à l'époque cela m'était indispensable, mais je ne me souviens plus pourquoi.
Je voulais démontrer que l'utilisation d'exit pouvait être délicate. Lors de la discussion précédente, j'avais oublié que c'était l'utilisation conjointe de exit et de atexit qui était explosive.
Je ne sais plus exactement de quand date ce code - tous les fichiers sont au 01/01/1970 00:00 - mais il a plus de dix ans et c'était mes tous premiers débuts en C++, avec Tubo C++ de Borland, sous MS/DOS. J'ai donc appri ce jour là que l'utilisation d'exit dans les destructeurs était dangereuse.
drkm wrote:
bernard tatin writes:
#define DEL_PTR(p) if (p) { delete p; p=0; }
Le test est inutile.
extern "C" void onexit(void) { DEL_PTR(e1);
Obfuscation.
DEL_PTR(e2); DEL_PTR(e3); } int main () { atexit(onexit);
Pourquoi déclarer un handler de atexit « extern "C" » ?
Pourquoi déclarer un handler de atexit « extern "C" » ?
Parce que c'est peut-être nécessaire ?
Selon §17.4.2.2/2 « It is unspecified whether a name from the Standard C library declared with external linkage has either extern "C" or extern "C++" linkage. Si l'en-tête déclare :
extern "C" int atexit( void (*func)() ) ;
la fonction que tu passes doit obligatoirement être extern "C" -- sinon, c'est une erreur, et le compilateur doit gueuler. Si on revanche, l'en-tête déclare simplement :
extern int atexit( void (*func)() ) ;
(extern "C++" étant le défaut), la fonction que tu passe ne doit en aucun cas être extern "C" -- sinon, c'est une erreur, et le compilateur doit gueuler.
En fait, je ne suis pas sûr. Dans §18.3, il y a deux signatures données. Mais ce n'est pas clair pour moi si c'est simplement pour montrer les deux signatures possibles, ou c'est pour dire qu'une implémentation doit obligatoirement en fournir les deux. (Dans d'autres cas où la norme exige le remplacement d'une fonction C par deux en C++, par exemple, dans §21.4/4, elle le dit explicitement.)
À vrai dire, je crois que ça vaut la peine de soulever la question dans comp.std.c++.
De la norme C, §7.20.4.3/2 : « The exit function causes normal program termination to occur. If more than one call to the exit function is executed by a program, the behavior is undefined. » (La norme C++ spécifie « additional behavior » pour atexit, par rapport à C. J'interprète « additional behavior » à signifier que les mêmes restrictions s'appliquent qu'en C.)
Dans l'ensemble, je dirais que atexit n'a aucun intérêt en C++ -- des destructeurs des objets statiques sont plus idiomatique, et convient plus. Heureusement, parce que je crois qu'elle est à peu près inutilisable.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
drkm <usenet.fclcxx@fgeorges.org> wrote in message
news:<wkr7nia05f.fsf@fgeorges.org>...
bernard tatin <bernard.tatin@nospam.tele2.fr> writes:
int main () {
atexit(onexit);
Pourquoi déclarer un handler de atexit « extern "C" » ?
Parce que c'est peut-être nécessaire ?
Selon §17.4.2.2/2 « It is unspecified whether a name from the Standard C
library declared with external linkage has either extern "C" or extern
"C++" linkage. Si l'en-tête déclare :
extern "C" int atexit( void (*func)() ) ;
la fonction que tu passes doit obligatoirement être extern "C" -- sinon,
c'est une erreur, et le compilateur doit gueuler. Si on revanche,
l'en-tête déclare simplement :
extern int atexit( void (*func)() ) ;
(extern "C++" étant le défaut), la fonction que tu passe ne doit en
aucun cas être extern "C" -- sinon, c'est une erreur, et le compilateur
doit gueuler.
En fait, je ne suis pas sûr. Dans §18.3, il y a deux signatures données.
Mais ce n'est pas clair pour moi si c'est simplement pour montrer les
deux signatures possibles, ou c'est pour dire qu'une implémentation doit
obligatoirement en fournir les deux. (Dans d'autres cas où la norme
exige le remplacement d'une fonction C par deux en C++, par exemple,
dans §21.4/4, elle le dit explicitement.)
À vrai dire, je crois que ça vaut la peine de soulever la question dans
comp.std.c++.
De la norme C, §7.20.4.3/2 : « The exit function causes normal program
termination to occur. If more than one call to the exit function is
executed by a program, the behavior is undefined. » (La norme C++
spécifie « additional behavior » pour atexit, par rapport à C.
J'interprète « additional behavior » à signifier que les mêmes
restrictions s'appliquent qu'en C.)
Dans l'ensemble, je dirais que atexit n'a aucun intérêt en C++ -- des
destructeurs des objets statiques sont plus idiomatique, et convient
plus. Heureusement, parce que je crois qu'elle est à peu près
inutilisable.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Pourquoi déclarer un handler de atexit « extern "C" » ?
Parce que c'est peut-être nécessaire ?
Selon §17.4.2.2/2 « It is unspecified whether a name from the Standard C library declared with external linkage has either extern "C" or extern "C++" linkage. Si l'en-tête déclare :
extern "C" int atexit( void (*func)() ) ;
la fonction que tu passes doit obligatoirement être extern "C" -- sinon, c'est une erreur, et le compilateur doit gueuler. Si on revanche, l'en-tête déclare simplement :
extern int atexit( void (*func)() ) ;
(extern "C++" étant le défaut), la fonction que tu passe ne doit en aucun cas être extern "C" -- sinon, c'est une erreur, et le compilateur doit gueuler.
En fait, je ne suis pas sûr. Dans §18.3, il y a deux signatures données. Mais ce n'est pas clair pour moi si c'est simplement pour montrer les deux signatures possibles, ou c'est pour dire qu'une implémentation doit obligatoirement en fournir les deux. (Dans d'autres cas où la norme exige le remplacement d'une fonction C par deux en C++, par exemple, dans §21.4/4, elle le dit explicitement.)
À vrai dire, je crois que ça vaut la peine de soulever la question dans comp.std.c++.
De la norme C, §7.20.4.3/2 : « The exit function causes normal program termination to occur. If more than one call to the exit function is executed by a program, the behavior is undefined. » (La norme C++ spécifie « additional behavior » pour atexit, par rapport à C. J'interprète « additional behavior » à signifier que les mêmes restrictions s'appliquent qu'en C.)
Dans l'ensemble, je dirais que atexit n'a aucun intérêt en C++ -- des destructeurs des objets statiques sont plus idiomatique, et convient plus. Heureusement, parce que je crois qu'elle est à peu près inutilisable.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
bernard tatin
wrote:
Parce que c'est peut-être nécessaire ?
Selon §17.4.2.2/2 « It is unspecified whether a name from the Standard C library declared with external linkage has either extern "C" or extern "C++" linkage. Si l'en-tête déclare :
extern "C" int atexit( void (*func)() ) ;
la fonction que tu passes doit obligatoirement être extern "C" -- sinon, c'est une erreur, et le compilateur doit gueuler.
Lorsque j'ai écrit ce code, il y a longtemps, cela devait être nécessaire. Avec les environnements d'aujourd'hui, ce n'est peut-être plus le cas.
Si j'utilisais atexit à l'époque : ce code détournait des interruptions. Cette partie très bas niveau n'utilisait pas de classes et le atexit faisait le nettoyage. Mon erreur a été de méler du C pur - avec assembleur en ligne - et du C++ sans trop de discernement. Je débutais en C++, aussi.
Bernard.
kanze@gabi-soft.fr wrote:
Parce que c'est peut-être nécessaire ?
Selon §17.4.2.2/2 « It is unspecified whether a name from the Standard C
library declared with external linkage has either extern "C" or extern
"C++" linkage. Si l'en-tête déclare :
extern "C" int atexit( void (*func)() ) ;
la fonction que tu passes doit obligatoirement être extern "C" -- sinon,
c'est une erreur, et le compilateur doit gueuler.
Lorsque j'ai écrit ce code, il y a longtemps, cela devait être
nécessaire. Avec les environnements d'aujourd'hui, ce n'est peut-être
plus le cas.
Si j'utilisais atexit à l'époque : ce code détournait des interruptions.
Cette partie très bas niveau n'utilisait pas de classes et le atexit
faisait le nettoyage. Mon erreur a été de méler du C pur - avec
assembleur en ligne - et du C++ sans trop de discernement. Je débutais
en C++, aussi.
Selon §17.4.2.2/2 « It is unspecified whether a name from the Standard C library declared with external linkage has either extern "C" or extern "C++" linkage. Si l'en-tête déclare :
extern "C" int atexit( void (*func)() ) ;
la fonction que tu passes doit obligatoirement être extern "C" -- sinon, c'est une erreur, et le compilateur doit gueuler.
Lorsque j'ai écrit ce code, il y a longtemps, cela devait être nécessaire. Avec les environnements d'aujourd'hui, ce n'est peut-être plus le cas.
Si j'utilisais atexit à l'époque : ce code détournait des interruptions. Cette partie très bas niveau n'utilisait pas de classes et le atexit faisait le nettoyage. Mon erreur a été de méler du C pur - avec assembleur en ligne - et du C++ sans trop de discernement. Je débutais en C++, aussi.
Bernard.
drkm
writes:
drkm wrote in message news:...
Pourquoi déclarer un handler de atexit « extern "C" » ?
En fait, je ne suis pas sûr. Dans §18.3, il y a deux signatures données.
C'est en effet ce que j'avais trouvé (je n'ai pas vérifié qu'il s'agit du même paragraphe, mais j'avais effectivement vu les deux signatures).
Mais ce n'est pas clair pour moi si c'est simplement pour montrer les deux signatures possibles, ou c'est pour dire qu'une implémentation doit obligatoirement en fournir les deux. (Dans d'autres cas où la norme exige le remplacement d'une fonction C par deux en C++, par exemple, dans §21.4/4, elle le dit explicitement.)
À vrai dire, je crois que ça vaut la peine de soulever la question dans comp.std.c++.
Sans doute. Mais je ne me sens pas l'étoffe « standardeeze ».
--drkm
kanze@gabi-soft.fr writes:
drkm <usenet.fclcxx@fgeorges.org> wrote in message
news:<wkr7nia05f.fsf@fgeorges.org>...
Pourquoi déclarer un handler de atexit « extern "C" » ?
En fait, je ne suis pas sûr. Dans §18.3, il y a deux signatures données.
C'est en effet ce que j'avais trouvé (je n'ai pas vérifié qu'il
s'agit du même paragraphe, mais j'avais effectivement vu les deux
signatures).
Mais ce n'est pas clair pour moi si c'est simplement pour montrer les
deux signatures possibles, ou c'est pour dire qu'une implémentation doit
obligatoirement en fournir les deux. (Dans d'autres cas où la norme
exige le remplacement d'une fonction C par deux en C++, par exemple,
dans §21.4/4, elle le dit explicitement.)
À vrai dire, je crois que ça vaut la peine de soulever la question dans
comp.std.c++.
Sans doute. Mais je ne me sens pas l'étoffe « standardeeze ».
Pourquoi déclarer un handler de atexit « extern "C" » ?
En fait, je ne suis pas sûr. Dans §18.3, il y a deux signatures données.
C'est en effet ce que j'avais trouvé (je n'ai pas vérifié qu'il s'agit du même paragraphe, mais j'avais effectivement vu les deux signatures).
Mais ce n'est pas clair pour moi si c'est simplement pour montrer les deux signatures possibles, ou c'est pour dire qu'une implémentation doit obligatoirement en fournir les deux. (Dans d'autres cas où la norme exige le remplacement d'une fonction C par deux en C++, par exemple, dans §21.4/4, elle le dit explicitement.)
À vrai dire, je crois que ça vaut la peine de soulever la question dans comp.std.c++.
Sans doute. Mais je ne me sens pas l'étoffe « standardeeze ».