Language linkage
Permet la liaison entre les unités de programme écrites dans différents langages de programmation.
|
Cela peut également être utilisé pour détacher une déclaration de son module. Voir Module ownership . |
(since C++20) |
extern
string-literal
{
declaration-seq
(optionnel)
}
|
(1) | ||||||||
extern
string-literal
declaration
|
(2) | ||||||||
| string-literal | - | un littéral de chaîne non évalué qui nomme la liaison de langage requise |
| declaration-seq | - | une séquence de déclarations, qui peut inclure des spécifications de liaison imbriquées |
| declaration | - | une déclaration |
Table des matières |
Explication
Chaque type de fonction, chaque nom de fonction avec liaison externe , et chaque nom de variable avec liaison externe , possède une propriété appelée liaison de langage . La liaison de langage encapsule l'ensemble des exigences nécessaires pour établir un lien avec une unité de programme écrite dans un autre langage de programmation : convention d'appel , algorithme de name mangling (décoration de noms), etc.
Seules deux liaisons de langage sont garanties d'être prises en charge :
- "C++" , la liaison de langage par défaut.
- "C" , qui permet de lier avec des fonctions écrites dans le langage de programmation C, et de définir, dans un programme C++, des fonctions qui peuvent être appelées depuis des unités écrites en C.
extern "C" { int open(const char *path_name, int flags); // Déclaration de fonction C } int main() { int fd = open("test.txt", 0); // appelle une fonction C depuis un programme C++ } // Cette fonction C++ peut être appelée depuis du code C extern "C" void handler(int) { std::cout << "Callback invoked\n"; // Elle peut utiliser C++ }
Puisque la liaison de langage fait partie de chaque type de fonction, les pointeurs vers des fonctions conservent également la liaison de langage. La liaison de langage des types de fonction (qui représente la convention d'appel) et la liaison de langage des noms de fonction (qui représente le name mangling) sont indépendantes l'une de l'autre :
extern "C" void f1(void(*pf)()); // déclare une fonction f1 avec liaison C, // qui retourne void et prend un pointeur vers une fonction C // qui retourne void et ne prend aucun paramètre extern "C" typedef void FUNC(); // déclare FUNC comme un type de fonction C qui retourne void // et ne prend aucun paramètre FUNC f2; // le nom f2 a une liaison C++, mais son type est fonction C extern "C" FUNC f3; // le nom f3 a une liaison C et son type est fonction C void() void (*pf2)(FUNC*); // le nom pf2 a une liaison C++, et son type est // "pointeur vers une fonction C++ qui retourne void et prend un // argument de type 'pointeur vers la fonction C qui retourne void // et ne prend aucun paramètre'" extern "C" { static void f4(); // le nom de la fonction f4 a une liaison interne (pas de langage) // mais le type de la fonction a une liaison de langage C }
Si deux déclarations d'une entité lui attribuent des liaisons de langage différentes, le programme est mal formé ; aucun diagnostic n'est requis si aucune déclaration n'est accessible depuis l'autre. Une redéclaration d'une entité sans spécification de liaison hérite de la liaison de langage de l'entité et de son type (s'il existe).
extern "C" int f(); extern "C++" int f(); // Erreur : liaisons linguistiques différentes extern "C" int g(); int g(); // OK, possède une liaison linguistique C int h(); // possède une liaison linguistique C++ par défaut extern "C" int h(); // Erreur : liaisons linguistiques différentes
Règles spéciales pour la liaison "C"
Lorsque des membres de classe , des fonctions amies avec une clause requires de fin , (depuis C++20) ou des fonctions membres non statiques apparaissent dans un bloc de langage "C" , la liaison de leurs types reste "C++" (mais les types de paramètres, s'il y en a, restent "C" ):
extern "C" { class X { void mf(); // la fonction mf et son type ont une liaison de langage C++ void mf2(void(*)()); // la fonction mf2 a une liaison de langage C++ ; // le paramètre a le type « pointeur vers fonction C » }; } template<typename T> struct A { struct B; }; extern "C" { template<typename T> struct A<T>::B { friend void f(B*) requires true {} // liaison de langage C ignorée }; } namespace Q { extern "C" void f(); // pas mal formé }
Soit
C
une déclaration qui déclare une fonction ou une variable avec une liaison de langage
"C"
. Si une autre déclaration
D
déclare une entité avec le même nom, et qu'elle satisfait l'une des conditions suivantes,
C
et
D
déclarent la même entité :
-
Ddéclare une variable qui appartient à la portée globale. -
Si
Cdéclare une variable,Ddéclare également une variable. -
Si
Cdéclare une fonction,Ddéclare également une fonction.
Contrairement aux
redéclarations régulières
,
C
et
D
peuvent avoir des
portées cibles
différentes :
extern "C" { int x; int f(); int g() { return 1; } } namespace A { int x; // Erreur : redéfinit « x » int f(); // OK, redéclare « f » int g() { return 1; } // Erreur : redéfinit « g » }
Cependant, les restrictions de telles déclarations s'appliquent toujours, ce qui signifie qu'elles doivent soit déclarer des fonctions, soit déclarer des variables, et les entités déclarées doivent avoir le même type :
namespace A { extern "C" int x(); extern "C" int y(); } int x; // Erreur : redéclare « x » comme une entité de type différent namespace B { void y(); // Erreur : redéclare « y » avec un type différent }
Notes
Les spécifications de langage ne peuvent apparaître que dans une portée d'espace de noms .
Les accolades de la spécification du langage n'établissent pas une portée.
Lorsque les spécifications de langage s'imbriquent, la spécification la plus interne est celle qui est en vigueur.
Une déclaration directement contenue dans une spécification de liaison de langage est traitée comme si elle contenait le extern specifier aux fins de déterminer la linkage du nom déclaré et s'il s'agit d'une definition .
extern "C" int x; // une déclaration et non une définition // La ligne ci-dessus est équivalente à extern "C" { extern int x; } extern "C" { int x; } // une déclaration et définition extern "C" double f(); static double f(); // erreur : conflit de linkage extern "C" static void g(); // erreur : conflit de linkage
extern "C" permet d'inclure des fichiers d'en-tête contenant des déclarations de fonctions de bibliothèque C dans un programme C++, mais si le même fichier d'en-tête est partagé avec un programme C, extern "C" (qui n'est pas autorisé en C) doit être masqué avec un #ifdef approprié, généralement __cplusplus :
#ifdef __cplusplus extern "C" int foo(int, int); // Le compilateur C++ voit ceci #else int foo(int, int); // Le compilateur C voit ceci #endif
Le seul compilateur moderne qui différencie les types de fonctions avec les liaisons linguistiques "C" et "C++" est Oracle Studio ; les autres ne permettent pas les surcharges qui ne diffèrent que par la liaison linguistique, y compris les ensembles de surcharge requis par la norme C++ ( std::qsort , std::bsearch , std::signal , std::atexit , et std::at_quick_exit ) : GCC bug 2316 , Clang bug 6277 , CWG issue 1555 .
extern "C" using c_predfun = int(const void*, const void*); extern "C++" using cpp_predfun = int(const void*, const void*); // mal formé, mais accepté par la plupart des compilateurs static_assert(std::is_same<c_predfun, cpp_predfun>::value, "Les liaisons de langage C et C++ ne doivent pas différencier les types de fonctions."); // les déclarations suivantes ne déclarent pas de surcharges dans la plupart des compilateurs // car c_predfun et cpp_predfun sont considérés comme étant le même type void qsort(void* base, std::size_t nmemb, std::size_t size, c_predfun* compar); void qsort(void* base, std::size_t nmemb, std::size_t size, cpp_predfun* compar);
Mots-clés
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 4 | C++98 | les noms avec liaison interne peuvent avoir des liaisons de langage | limité aux noms avec liaison externe |
| CWG 341 | C++98 |
une fonction avec liaison de langage
"C"
peut
avoir le même nom qu'une variable globale |
le programme est mal formé dans ce cas
(aucun diagnostic requis s'ils apparaissent dans différentes unités de traduction) |
| CWG 564 | C++98 |
le programme était mal formé si deux déclarations
différaient uniquement par les spécifications de liaison de langage (c'est-à-dire différents littéraux de chaîne suivant 'extern') |
les liaisons de langage réelles données par
les déclarations sont comparées à la place |
| CWG 2460 | C++20 |
les fonctions amies avec une clause
requires
finale
et liaison de langage "C" avaient des comportements conflictuels |
"C"
liaison de langage
est ignorée dans ce cas |
| CWG 2483 | C++98 |
la liaison des types des fonctions membres statiques
apparaissant dans des blocs de langage "C" était "C++" |
la liaison est "C" |
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 9.11 Spécifications de liaison [dcl.link]
- Norme C++20 (ISO/CEI 14882:2020) :
-
- 9.11 Spécifications de liaison [dcl.link]
- Norme C++17 (ISO/IEC 14882:2017) :
-
- 10.5 Spécifications de liaison [dcl.link]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 7.5 Spécifications de liaison [dcl.link]
- Norme C++11 (ISO/IEC 14882:2011) :
-
- 7.5 Spécifications de liaison [dcl.link]
- Norme C++03 (ISO/CEI 14882:2003) :
-
- 7.5 Spécifications de liaison [dcl.link]
- Norme C++98 (ISO/CEI 14882:1998) :
-
- 7.5 Spécifications de liaison [dcl.link]