Les exceptions C ++ vont-elles se propager en toute sécurité via le code C?

J’ai une application C ++ qui appelle SQLite (SQLite est en C) sqlite3_exec () qui peut à son tour appeler ma fonction de rappel implémentée en C ++. SQLite est compilé dans une bibliothèque statique.

Si une exception échappe à mon rappel, va-t-elle se propager en toute sécurité via le code C de SQLite au code C ++ appelant sqlite3_exec ()?

Je suppose que cela dépend du compilateur. Cependant, lancer une exception dans le rappel serait une très mauvaise idée. Soit cela ne fonctionnera pas, soit le code C de la bibliothèque SQLite ne pourra pas le gérer. Considérez s’il s’agit d’un code dans SQLite:

{ char * p = malloc( 1000 ); ... call_the_callback(); // might throw an exception ... free( p ); } 

Si l’exception “fonctionne”, le code C n’a aucun moyen de l’attraper et p ne sera jamais libéré. Il en va de même pour les autres ressources allouées par la bibliothèque, bien entendu.

Il existe déjà un protocole de rappel pour annuler l’appel API. De la docs :

Si un rappel sqlite3_exec () renvoie une valeur autre que zéro, la routine sqlite3_exec () renvoie SQLITE_ABORT sans appeler à nouveau le rappel ni exécuter aucune instruction SQL ultérieure.

Je vous recommande fortement d’utiliser ceci au lieu d’une exception.

SQLite s’attend à ce que vous retourniez un SQLITE_ABORT en cas d’erreur et un code de retour 0 pour l’absence d’erreur. Donc, vous devriez envelopper tout votre rappel C ++ dans un catch try . Puis, dans la capture, renvoyer un code d’erreur SQLite SQLITE_ABORT, sinon un zéro.

Des problèmes se poseront si vous ignorez le renvoi via SQLite car cela ne libérera pas / ne complétera pas le code qu’il aura après votre retour de votre rappel. Cela causera potentiellement des problèmes incalculables, dont certains peuvent être très obscurs.

C’était une question très intéressante et je l’ai testée par curiosité. Sous OS X w / gcc 4.2.1, la réponse était OUI. Ça fonctionne parfaitement. Je pense qu’un vrai test utiliserait gcc pour le C ++ et un autre (MSVC ?, LLVM?) Pour la partie C et voir si cela fonctionne toujours.

Mon code:

callb.h:

 #ifdef __cplusplus extern "C" { #endif typedef void (*t_callb)(); void cfun(t_callb fn); #ifdef __cplusplus } #endif 

callb.c:

 #include "callb.h" void cfun(t_callb fn) { fn(); } 

main.cpp:

 #include  #include  #include "callb.h" void myfn() { std::ssortingng s( "My Callb Except" ); throw s; } int main() { try { cfun(myfn); } catch(std::ssortingng s) { std::cout << "Caught: " << s << std::endl; } return 0; } 

Si votre callback appelé depuis sqlite provient du même fil que celui que vous avez appelé sqlite3_exec (), un lancer quelque part dans la stack d’appels devrait être attrapé par un catch de niveau supérieur.

Tester cela vous-même devrait être simple, non?

[edit] Après avoir creusé un peu plus moi-même, j’ai découvert que la norme C ++ est quelque peu vague sur le comportement qu’une fonction c ++ appelée depuis c devrait avoir lors du lancement d’une exception.

Vous devez absolument utiliser le mécanisme de traitement des erreurs attendu par l’API. Sinon, l’API elle-même sera principalement dans un état non défini et tout appel ultérieur pourrait éventuellement échouer / se bloquer.