Fonctions Lippincott

L'interopérabilité entre C et C++ se passe souvent à la frontière des interactions entre ces langages. Cette interopérabilité se passe généralement bien, dans la mesure où certaines règles sont suivies pour éviter des « cassures conceptuelles » : il faut faire en sorte que les frontières entre les langages se limitent à des éléments que les programmes écrits dans chacun des langages impliqués « comprendront » et traiteront sur les mêmes bases sémantiques :

Un important problème conceptuel s'ajoutant à ces considérations est celui des exceptions : les exceptions sont un mécanisme important de gestion des cas inhabituels en C++, alors que C ne les connaît pas et n'est pas en mesure de réagir à leur occurrence.

On reconnaît à Lisa Lippincott la parenté d'un formalisme de fonctions capables de permettre à C et à C++ de transiger l'un avec l'autre malgré leur divergence de fond sur le plan de la reconnaissance et de la gestion des exceptions. Vu de manière aérienne, une fonction Lippincott est une fonction C++ (car ce langage ratisse plus large que C, et est donc plus en mesure de couvrir les zones que C ne pourrait prendre en charge) qui joue le rôle d'intermédiaire entre les deux langages en faisant apparaître une exception C++ comme un code d'erreur C, et en traduisant les erreurs C en exceptions C++.

Client C, serveur C++

Si une fonction C appelle une fonction C++, la fonction Lippincott qui jouera le rôle d'intermédiaire pourra se présenter comme suit :

Nombre fini et connu de cas d'exceptions Nombre fini et connu de cas d'exceptions (variante) Nombre inconnu de cas d'exceptions
class A {};
class B {};
class C {};
int fonction_cplusplus(); // lève A, B ou C
extern "C" {
   const int RESULTAT_ERRONNE = -1; // disons
   enum Erreur { TYPE_A, TYPE_B, TYPE_C, TYPE_INCONNU };
   int visage_pour_code_c() {
      try {
         return fonction_cplusplus();
      } catch(A&) {
         return TYPE_A;
      } catch(B&) {
         return TYPE_B;
      } catch(C&) {
         return TYPE_C;
      }
      return TYPE_INCONNU;
   }
}
class A {};
class B {};
class C {};
int fonction_cplusplus(); // lève A, B ou C
extern "C" {
   const int RESULTAT_ERRONNE = -1; // disons
   enum Erreur { TYPE_A, TYPE_B, TYPE_C, TYPE_INCONNU };
   int extraire_erreur() {
      try {
         throw; // si une exception est en vol, on la relève
      } catch(A&) {
         return TYPE_A;
      } catch(B&) {
         return TYPE_B;
      } catch(C&) {
         return TYPE_C;
      }
      return RESULTAT_ERRONNE;
   }
   int visage_pour_code_c() {
      try {
         return fonction_cplusplus();
      } catch(...) {
         return extraire_erreur();
      }
   }
}
int fonction_cplusplus(); // lève peut-être... quelque chose
extern "C" {
   const int RESULTAT_ERRONNE = -1; // disons
   int visage_pour_code_c() {
      try {
         return fonction_cplusplus();
      } catch(...) {
         return TYPE_INCONNU;
      }
   }
}

Ici, un programme C appellera visage_pour_code_c() qui appellera, à l'insu du code client, fonction_cplusplus(). Si une exception est levée en cours de route, le client de visage_pour_code_c() recevra soit le résultat, soit un code d'erreur.

Client C++, serveur C

Si une fonction C++ appelle une fonction C, la fonction Lippincott qui jouera le rôle d'intermédiaire pourra se présenter comme suit :

Nombre fini et connu de cas d'erreurs Nombre inconnu de cas d'erreurs
class A {};
class B {};
class C {};
class Inconnu {};
extern "C" {
   const int RESULTAT_ERRONNE = -1; // disons
   enum Erreur { TYPE_A, TYPE_B, TYPE_C, TYPE_INCONNU };
   int fonction_c(); // retourne le resultat ou un code d'erreur
}
int visage_cplusplus() {
   int resultat = fonction_c();
   if (resultat == TYPE_A)
      throw A{};
   if (resultat == TYPE_B)
      throw B{};
   if (resultat == TYPE_C)
      throw C{};
   if (resultat == RESULTAT_ERRONNE)
      throw Inconnu{};
   return resultat;
}
class Inconnu {};
extern "C" {
   const int RESULTAT_ERRONNE = -1; // disons
   int fonction_c(); // retourne le resultat ou un code d'erreur
}
int visage_cplusplus() {
   int resultat = fonction_c();
   if (resultat == RESULTAT_ERRONNE)
      throw Inconnu{};
   return resultat;
}

Lectures complémentaires

Pour une présentation sur le sujet par Lisa Lippincott elle-même pendant CppCon 2014, voir https://www.youtube.com/watch?v=3ZO0V4Prefc (ou https://github.com/CppCon/CppCon2014/tree/master/Presentations/How%20to%20call%20C%20libraries%20from%20C%2B%2B)

Quelques liens pour enrichir le propos.


Valid XHTML 1.0 Transitional

CSS Valide !