Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Melanger C et C++ : exceptions ou setjmp ?

11 réponses
Avatar
Fabien LE LEZ
Bonjour,

J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).
La gestion d'erreurs dans cette bibliothèque fonctionne comme suit :
on fournit une fonction "my_error_exit()", qui doit se démerder pour
arrêter le code. (La fonction par défaut appelle exit().)

Dans cette fonction, je vois deux choix possibles :
1- longjmp() (avec un setjmp() dans le code appelant)
2- throw une_exception;

Au moins sur mon compilateur (VC++ 2008), la première solution est
officiellement non gérée :
"Do not use setjmp and longjmp in C++ programs; these functions do not
support C++ object semantics."
<http://msdn.microsoft.com/en-us/library/yz2ez4as(VS.80).aspx>

Lancer une exception serait bien agréable, mais j'ai cru lire qu'il ne
fallait pas laisser se propager une exception à travers du code C.


Pour l'instant, j'ai pondu une interface en C qui contient un paquet
de fonctions de ce style :

boolean JpegStartDecompress (struct jpeg_decompress_struct* cinfo)
{
if (setjmp (error_manager.setjmp_buffer))
{
return 0;
}

jpeg_start_decompress (cinfo);
return 1;
}


Si quelqu'un a une meilleure solution...

Merci d'avance.

10 réponses

1 2
Avatar
pjb
Fabien LE LEZ writes:

Bonjour,

J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).
La gestion d'erreurs dans cette bibliothèque fonctionne comme suit :
on fournit une fonction "my_error_exit()", qui doit se démerder pour
arrêter le code. (La fonction par défaut appelle exit().)

Dans cette fonction, je vois deux choix possibles :
1- longjmp() (avec un setjmp() dans le code appelant)
2- throw une_exception;

Au moins sur mon compilateur (VC++ 2008), la première solution est
officiellement non gérée :
"Do not use setjmp and longjmp in C++ programs; these functions do not
support C++ object semantics."
<http://msdn.microsoft.com/en-us/library/yz2ez4as(VS.80).aspx>

Lancer une exception serait bien agréable, mais j'ai cru lire qu'il ne
fallait pas laisser se propager une exception à travers du code C.


Pour l'instant, j'ai pondu une interface en C qui contient un paquet
de fonctions de ce style :

boolean JpegStartDecompress (struct jpeg_decompress_struct* cinfo)
{
if (setjmp (error_manager.setjmp_buffer))
{
return 0;
}

jpeg_start_decompress (cinfo);
return 1;
}


Si quelqu'un a une meilleure solution...



Compiler la bibliothèque avec un compilateur C++ et utiliser les exceptions.

--
__Pascal Bourguignon__
Avatar
David Côme
On Mon, 07 Jul 2008 11:43:34 +0200, Pascal J. Bourguignon
wrote:

Fabien LE LEZ writes:

Bonjour,

J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).
La gestion d'erreurs dans cette bibliothèque fonctionne comme suit :
on fournit une fonction "my_error_exit()", qui doit se démerder pour
arrêter le code. (La fonction par défaut appelle exit().)

Dans cette fonction, je vois deux choix possibles :
1- longjmp() (avec un setjmp() dans le code appelant)
2- throw une_exception;

Au moins sur mon compilateur (VC++ 2008), la première solution est
officiellement non gérée :
"Do not use setjmp and longjmp in C++ programs; these functions do not
support C++ object semantics."
<http://msdn.microsoft.com/en-us/library/yz2ez4as(VS.80).aspx>

Lancer une exception serait bien agréable, mais j'ai cru lire qu'il ne
fallait pas laisser se propager une exception à travers du code C.


Pour l'instant, j'ai pondu une interface en C qui contient un paquet
de fonctions de ce style :

boolean JpegStartDecompress (struct jpeg_decompress_struct* cinfo)
{
if (setjmp (error_manager.setjmp_buffer))
{
return 0;
}

jpeg_start_decompress (cinfo);
return 1;
}


Si quelqu'un a une meilleure solution...



Compiler la bibliothèque avec un compilateur C++ et utiliser les
exceptions.




Très beau en théorie, mais en pratique, au moindre malloc sans cast, ca va
casser.
Et je passe sous silence les autres problèmes entre C et C++.
Avatar
Guillaume GOURDIN
Tu peux utiliser les membres 'client_data' de la structure
'jpeg_decompress_struct' et 'error_exit' de la structure
'jpeg_error_mgr' pour personnaliser ta gestion des erreurs:

METHODDEF(void) my_error_exit(j_common_ptr cinfo)
{
ggo_error * error;
error = static_cast<ggo_error*>( cinfo->client_data );
*error = GGO_ERROR_IO;
}

ggo_error read_jpeg(const string & filename)
{
jpeg_decompress_struct cinfo;
jpeg_error_mgr jerr;
ggo_error error = GGO_ERROR_NONE;

cinfo.err = jpeg_std_error( &jerr );
cinfo.client_data = &error;
jerr.error_exit = my_error_exit;
jpeg_create_decompress( &cinfo );

if ( error != GGO_ERROR_NONE) {
return ( error );
}

// Etc.

Rien ne t'empêche (j'imagine) d'avoir un traitement des erreurs plus fin
à base d'exceptions. Je te conseille également de lire la doc qui est
assez détaillée à ce niveau.







Fabien LE LEZ wrote:
Bonjour,

J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).
La gestion d'erreurs dans cette bibliothèque fonctionne comme suit :
on fournit une fonction "my_error_exit()", qui doit se démerder pour
arrêter le code. (La fonction par défaut appelle exit().)

Dans cette fonction, je vois deux choix possibles :
1- longjmp() (avec un setjmp() dans le code appelant)
2- throw une_exception;

Au moins sur mon compilateur (VC++ 2008), la première solution est
officiellement non gérée :
"Do not use setjmp and longjmp in C++ programs; these functions do not
support C++ object semantics."
<http://msdn.microsoft.com/en-us/library/yz2ez4as(VS.80).aspx>

Lancer une exception serait bien agréable, mais j'ai cru lire qu'il ne
fallait pas laisser se propager une exception à travers du code C.


Pour l'instant, j'ai pondu une interface en C qui contient un paquet
de fonctions de ce style :

boolean JpegStartDecompress (struct jpeg_decompress_struct* cinfo)
{
if (setjmp (error_manager.setjmp_buffer))
{
return 0;
}

jpeg_start_decompress (cinfo);
return 1;
}


Si quelqu'un a une meilleure solution...

Merci d'avance.



Avatar
James Kanze
On Jul 7, 10:04 am, Fabien LE LEZ wrote:
J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).
La gestion d'erreurs dans cette bibliothèque fonctionne comme
suit : on fournit une fonction "my_error_exit()", qui doit se
démerder pour arrêter le code. (La fonction par défaut appelle
exit().)



Dans cette fonction, je vois deux choix possibles :
1- longjmp() (avec un setjmp() dans le code appelant)
2- throw une_exception;



Au moins sur mon compilateur (VC++ 2008), la première solution est
officiellement non gérée :
"Do not use setjmp and longjmp in C++ programs; these functions do not
support C++ object semantics."
<http://msdn.microsoft.com/en-us/library/yz2ez4as(VS.80).aspx>



En somme, elles n'appellent pas les destructeurs. Ça, on le
savait déjà.

Lancer une exception serait bien agréable, mais j'ai cru lire
qu'il ne fallait pas laisser se propager une exception à
travers du code C.



Ça dépend de l'implémentation. Sur la plupart des
implémentations, ça ne pose pas de problèmes, sauf... il n'y a
pas d'exceptions en C, et donc, le code s'attend probablement à
ce que quand il appelle une fonction, cette fonction retourne
d'où elle a été appelée (de façon à ce qu'il fait le ménage).

Pour l'instant, j'ai pondu une interface en C qui contient un
paquet de fonctions de ce style :



boolean JpegStartDecompress (struct jpeg_decompress_struct* cinfo)
{
if (setjmp (error_manager.setjmp_buffer))
{
return 0;
}

jpeg_start_decompress (cinfo);
return 1;
}



Si quelqu'un a une meilleure solution...



Tu n'as pas cité la documentation réele. Qu'est-ce qu'elle dit
réelement à la place de « se démerder pour arrêter le code ».
J'ai comme une forte doute que ce que ça veut dire, c'est qu'il
faut arrêter le code, dans le sens de terminer le processus
(pour que les ressources soient libérées, etc.). C-à-d que tu
as, en gros, le choix entre exit(), abort() ou ExitProcess()
(sous Windows), avec éventuellement l'édition d'un message de
log, etc., avant.

OK, j'ai régardé dans la doc, et elle dit que longjmp est OK.
Dans ce cas-là, il est probable aussi qu'une exception
marcherait, mais pourquoi prendre la risque. D'après ce que j'ai
lu, ils ont prévu la possiblité des données client dans la
structure de contrôle ; sers-t-en pour passer les informations
qui t'intéressent, puis lever l'exception dans le cas d'erreur
de setjmp.

--
James Kanze (GABI Software) email:
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
Avatar
Sylvain SF
Fabien LE LEZ wrote on 07/07/2008 10:04:

J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).



ce n'est pas un nom univoque, plusieurs distribs portent ce nom
(ou ils existent plusieurs branches) de laquelle parle-t-on ?

La gestion d'erreurs dans cette bibliothèque fonctionne comme suit :
on fournit une fonction "my_error_exit()", qui doit se démerder pour
arrêter le code. (La fonction par défaut appelle exit().)



j'ai transcris en C++ une partie de la lib IJG Jpeg-6a il y a qlq années
(partie compression uniquement), mis à part les appels invalides (que je
traite en amont) seules les allocations nécessitaient un gestionnaire
d'erreurs, je les ai regroupé et isolé par try catch.

il n'est pas toujours indispensable de "mélanger c et c++", et si l'on
peux s'en passer (parce que les srcs sont dispo) c'est sûrement mieux.

Sylvain.
Avatar
Fabien LE LEZ
On Mon, 7 Jul 2008 07:20:18 -0700 (PDT), James Kanze
:

En somme, elles n'appellent pas les destructeurs. Ça, on le
savait déjà.



En fait, ayant très peu d'expérience en C, c'est la première fois que
je contemplais l'idée d'utiliser longjmp().

mais j'ai cru lire
qu'il ne fallait pas laisser se propager une exception à
travers du code C.



Ça dépend de l'implémentation. Sur la plupart des
implémentations, ça ne pose pas de problèmes, sauf... il n'y a
pas d'exceptions en C, et donc, le code s'attend probablement à
ce que quand il appelle une fonction, cette fonction retourne
d'où elle a été appelée (de façon à ce qu'il fait le ménage).



Ici, comme la doc préconise longjmp(), ça ne devrait pas poser de
problème. Donc, en théorie, je devrais pouvoir lancer une exception,
et lui faire traverser le code.


Pour l'instant, j'ai pondu une interface en C qui contient un
paquet de fonctions de ce style :



boolean JpegStartDecompress (struct jpeg_decompress_struct* cinfo)






D'après ce que j'ai
lu, ils ont prévu la possiblité des données client dans la
structure de contrôle ; sers-t-en pour passer les informations
qui t'intéressent,



En fait, pour l'instant au moins, je n'ai pas besoin d'autre
information que "la décompression a réussi" ou "elle a échoué".

puis lever l'exception dans le cas d'erreur de setjmp.



Si j'ai bien compris, tu préconises de compiler la fonction
JpegStartDecompress() en C++, et d'y lancer une exception ?

J'avais mis ces fonctions dans un .c, histoire de les faire compiler
par le compilateur C, et donc m'assurer que setjmp/longjmp ne pouvait
en aucun cas interférer avec quelque objet C++ que ce soit.
Avatar
Fabien LE LEZ
On Mon, 07 Jul 2008 23:02:03 +0200, Sylvain SF
:

Fabien LE LEZ wrote on 07/07/2008 10:04:

J'essaie d'utiliser une bibliothèque écrite en C (libjpeg).



de laquelle parle-t-on ?



La version présente sur le site de l'IJG.

j'ai transcris en C++ une partie de la lib IJG Jpeg-6a il y a qlq années
(partie compression uniquement), mis à part les appels invalides (que je
traite en amont) seules les allocations nécessitaient un gestionnaire
d'erreurs,



Malheureusement la décompression est une autre paire de manches,
puisqu'on peut avoir une erreur si le fichier n'est pas un fichier
JPEG correct.
Avatar
pjb
Fabien LE LEZ writes:
J'avais mis ces fonctions dans un .c, histoire de les faire compiler
par le compilateur C, et donc m'assurer que setjmp/longjmp ne pouvait
en aucun cas interférer avec quelque objet C++ que ce soit.



Ce n'est pas suffisant. Le problème n'est pas lexical, mais dynamique. Pour illustrer:


extern "C" {
typedef void (*fun)(void);
jmp_buf there;

void c_f2(fun f){
f();
if(bad()){ longjump(there); }
}

void c_f0(void){
SomeCResource* resource=SomeCResource_new();
if(setjmp(there)){
cpp_f1();
}else{
/* something is wrong here */
}
SomeCResource_free(resource);
}
}

void cpp_f1(void){
SomeCppResource resource;
c_f2(&cpp_f2);
/* important: ici resource est normalement "destructed" */
}

void cpp_f2(void){
if(bad()){ throw 42; }
}

int main(void){
try{
c_f0();
}catch(...){
/* something is wrong here */
}
}


Si longjmp est appelé, c'est avec une pile telle que:

| C c_f2
| C++ cpp_f1
| C c_f0
| C++ main
+-----------

le problème qui va se poser c'est que les destructeurs à la fin de
cpp_f1 ne seront pas appelés. On se retrouve avec une fuite de
l'instance de SomeCppResource.


Si throw est appelé, c'est avec une pile telle que:

| C++ cpp_f2
| C c_f2
| C++ cpp_f1
| C c_f0
| C++ main
+-----------

Dans ce cas, les destructeurs de cpp_f1 seront bien appelés, mais le
problème maintenant est que le code C protégé par setjmp ne le sera
pas: setjmp ne retournera jamais. On se retrouve avec une fuite de
l'instance de SomeCResource.


Mais si on s'arrange pour que le longjmp ne croise pas de cadre de
pile non C, alors il ne devrait pas y avoir de problème. Ta première
solution est bonne. À la limite, on pourrait faire:

void JpegStartDecompress (struct jpeg_decompress_struct* cinfo)
{
if(setjmp(error_manager.setjmp_buffer)){
jpeg_start_decompress(cinfo);
}else{
throw JpegError(error_manager);
}
}


--
__Pascal Bourguignon__
Avatar
Sylvain SF
Fabien LE LEZ wrote on 08/07/2008 11:00:

Malheureusement la décompression est une autre paire de manches,



non, elle est réalisée par de nombreuses routines OS (pour la plupart
des OS) et n'ai pas sujette à paramètre (on veux un DIB) donc je n'ai
pas cherché à inventer l'eau tiède en le recodant mais elle ne m'a pas
paru différemment manchée.

puisqu'on peut avoir une erreur si le fichier n'est pas un fichier
JPEG correct.



"fichier pas correct" est très vague !...

JPG est taggé (tout comme tiff), il est facile de vérifier la validité
du flux complet, vérifier individuellement chaque composant peut être
plus ardu pour certains d'entre eux, mais si tu penses que c'est là le
risque d'erreur, tu sais où mettre les try catch.

Sylvain.
Avatar
Sylvain SF
Fabien LE LEZ wrote on 08/07/2008 10:45:

J'avais mis ces fonctions dans un .c, histoire de les faire compiler
par le compilateur C, et donc m'assurer que setjmp/longjmp ne pouvait
en aucun cas interférer avec quelque objet C++ que ce soit.



pour conserver le noeud qui parassait te géner c'est efficace!

as-tu penser / essayer de tout mettre dans un .cpp, de grepper
les malloc en new et les on_error_xxx par un throw ?

Sylvain.
1 2