Que fait extern inline?

93

Je comprends que c'est inlineen soi une suggestion pour le compilateur, et à sa discrétion, il peut ou non intégrer la fonction, et il produira également du code objet pouvant être lié.

Je pense que cela static inlinefait la même chose (peut ou non en ligne) mais ne produira pas de code d'objet pouvant être lié lorsqu'il est en ligne (car aucun autre module ne peut y être lié).

extern inlines'inscrit-il dans l'image?

Supposons que je veuille remplacer une macro de préprocesseur par une fonction en ligne et exiger que cette fonction soit intégrée (par exemple, parce qu'elle utilise les macros __FILE__et __LINE__qui devraient résoudre pour l'appelant mais pas cette fonction appelée). Autrement dit, je veux voir une erreur de compilateur ou de l'éditeur de liens au cas où la fonction ne serait pas intégrée. Le extern inlinefait-il? (Je suppose que, si ce n'est pas le cas, il n'y a aucun moyen d'obtenir ce comportement autre que de s'en tenir à une macro.)

Existe-t-il des différences entre C ++ et C?

Existe-t-il des différences entre les différents fournisseurs et versions de compilateurs?

dragosht
la source

Réponses:

129

en K&R C ou C89, inline ne faisait pas partie du langage. De nombreux compilateurs l'ont implémenté comme une extension, mais il n'y avait pas de sémantique définie concernant son fonctionnement. GCC a été parmi les premiers à mettre en œuvre inline, et introduit la inline, static inlineet les extern inlineconstructions; la plupart des compilateurs pré-C99 suivent généralement son exemple.

GNU89:

  • inline: la fonction peut être en ligne (c'est juste un indice). Une version hors ligne est toujours émise et visible de l'extérieur. Par conséquent, vous ne pouvez avoir un tel inline défini que dans une unité de compilation, et chaque autre doit le voir comme une fonction hors ligne (ou vous obtiendrez des symboles en double au moment de la liaison).
  • extern inline ne générera pas de version hors ligne, mais pourrait en appeler une (que vous devez donc définir dans une autre unité de compilation. La règle de définition unique s'applique, cependant; la version hors ligne doit avoir le même code que la inline offert ici, au cas où le compilateur l'appellerait à la place.
  • static inlinene générera pas une version hors ligne visible de l'extérieur, bien qu'elle puisse générer un fichier statique. La règle à une définition ne s'applique pas, car il n'y a jamais de symbole externe émis ni d'appel à un.

C99 (ou GNU99):

  • inline: comme GNU89 "extern inline"; aucune fonction visible de l'extérieur n'est émise, mais une peut être appelée et doit donc exister
  • extern inline: comme GNU89 "inline": du code visible de l'extérieur est émis, donc au plus une unité de traduction peut l'utiliser.
  • static inline: comme GNU89 "statique en ligne". C'est le seul portable entre gnu89 et c99

C ++:

Une fonction qui est en ligne n'importe où doit être en ligne partout, avec la même définition. Le compilateur / éditeur de liens triera plusieurs instances du symbole. Il n'y a pas de définition de static inlineou extern inline, bien que de nombreux compilateurs les aient (généralement suivant le modèle gnu89).

puetzk
la source
2
Dans Classic Classic C, «inline» n'était pas un mot-clé; il était disponible pour être utilisé comme nom de variable. Cela s'appliquerait à C89 et pré-standard (K&R) C.
Jonathan Leffler
Vous avez raison, semble-t-il. Fixé. Je pensais qu'il avait été réservé comme mot-clé dans C89 (mais pas dans K&R), mais il me semble que je me suis mal souvenu
puetzk
J'aimerais ajouter que pour Visual C ++ de Microsoft, il existe un mot clé __forceinline qui imposera l'intégration de votre fonction. Il s'agit évidemment d'une extension spécifique au compilateur uniquement pour VC ++.
untitled8468927
Y a-t-il une différence entre C99 "extern inline" et aucun spécificateur?
Jo So
Sémantiquement non; tout comme une fonction non en ligne, extern inlineest soumise à la règle d'une définition unique, et c'est la définition. Mais si l'heuristique d'optimisation définie par l'implémentation suit la recommandation d'utiliser le inlinemot - clé comme une suggestion que "les appels à la fonction soient aussi rapides que possible" (ISO 9899: 1999 §6.7.4 (5), extern inlinecompte
puetzk
31

Je crois que vous avez mal compris __FILE__ et __LINE__ sur la base de cette déclaration:

car il utilise les macros __FILE__ et __LINE__ qui devraient résoudre pour l'appelant mais pas pour cette fonction appelée

Il existe plusieurs phases de compilation, et le prétraitement est le premier. __FILE__ et __LINE__ sont remplacés au cours de cette phase. Ainsi, au moment où le compilateur peut considérer la fonction à insérer, elle a déjà été remplacée.

Don Neufeld
la source
14

On dirait que vous essayez d'écrire quelque chose comme ceci:

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

et en espérant que vous obtiendrez différentes valeurs imprimées à chaque fois. Comme Don le dit, vous ne le ferez pas, car __FILE__ et __LINE__ sont implémentés par le préprocesseur, mais en ligne est implémenté par le compilateur. Ainsi, où que vous appeliez printLocation, vous obtiendrez le même résultat.

La seule façon de faire fonctionner cela est de faire de printLocation une macro. (Oui je sais...)

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...
Roddy
la source
18
Une astuce courante consiste pour une macro PRINT_LOCATION à appeler une fonction printLocation, en passant FILE et LINE comme paramètres. Cela peut entraîner un meilleur comportement du débogueur / éditeur / etc lorsque le corps de la fonction n'est pas trivial.
Steve Jessop
@Roddy Regardez ma solution - une extension de la vôtre mais plus complète et extensible.
enthusiasticgeek
@SteveJessop Quelque chose comme j'ai énuméré dans la solution ci-dessous?
enthousiastegeek
3

La situation avec inline, static inline et extern inline est compliquée, notamment parce que gcc et C99 définissent des significations légèrement différentes pour leur comportement (et probablement aussi C ++). Vous pouvez trouver des informations utiles et détaillées sur ce qu'ils font en C ici .

Simon Howard
la source
2

Les macros sont votre choix ici plutôt que les fonctions en ligne. Une occasion rare où les macros dominent les fonctions en ligne. Essayez ce qui suit: J'ai écrit ce code "MACRO MAGIC" et cela devrait fonctionner! Testé sur gcc / g ++ Ubuntu 10.04

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)

#ifdef __cplusplus

#include <cstdio>
#include <cstring>

#else

#include <stdio.h>
#include <string.h>

#endif

//=========== MACRO MAGIC BEGINS ============

//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )

#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))

#define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);

//=========== MACRO MAGIC ENDS ============

int main (int argc, char** argv) {

  LOG("Greetings StackOverflow! - from enthusiasticgeek\n");

  return 0;
}

Pour plusieurs fichiers, définissez ces macros dans un fichier d'en-tête distinct, y compris le même dans chaque fichier c / cc / cxx / cpp. Dans la mesure du possible, préférez les fonctions en ligne ou les identifiants const (selon le cas) aux macros.

enthousiaste
la source
2

Au lieu de répondre "que fait-il?", Je réponds "comment faire ce que je veux?" Il existe 5 types d'inlining, tous disponibles dans GNU C89, C99 standard et C ++:

toujours en ligne, sauf si l'adresse est prise

Ajoutez __attribute__((always_inline))à n'importe quelle déclaration, puis utilisez l'un des cas ci-dessous pour gérer la possibilité que son adresse soit prise.

Vous ne devriez probablement jamais l'utiliser, sauf si vous avez besoin de sa sémantique (par exemple pour affecter l'assemblage d'une certaine manière, ou pour l'utiliser alloca). Le compilateur sait généralement mieux que vous si cela en vaut la peine.

en ligne et émettre un symbole faible (comme C ++, alias "faites que ça marche")

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

Notez que cela laisse un tas de copies du même code traîner, et l'éditeur de liens en choisit une arbitrairement.

en ligne, mais n'émet jamais de symbole (laissant des références externes)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

émettre toujours (pour un TU, pour résoudre le précédent)

La version suggérée émet un symbole faible en C ++, mais un symbole fort dans l'un ou l'autre des dialectes de C:

void foo(void);
inline void foo(void) { ... }

Ou vous pouvez le faire sans l'indice, qui émet un symbole fort dans les deux langues:

void foo(void) { ... }

En règle générale, vous savez quelle langue est votre TU lorsque vous fournissez les définitions, et vous n'avez probablement pas besoin de beaucoup d'insertion.

en ligne et émettre dans chaque TU

static inline void foo(void) { ... }

Pour tous ceux-ci sauf celui- staticlà, vous pouvez ajouter une void foo(void)déclaration ci-dessus. Cela facilite la "meilleure pratique" d'écrire des en-têtes propres, puis de #includecréer un fichier séparé avec les définitions en ligne. Ensuite, si vous utilisez des lignes en ligne de style C, #defineune macro différemment dans une unité TU dédiée pour fournir les définitions hors ligne.

N'oubliez pas extern "C"si l'en-tête peut être utilisé à la fois en C et en C ++!

o11c
la source
Manquant: qu'en est-il de MSVC? Il a quelques extensions de dialecte C89, mais je n'utilise jamais MSVC et ne sais pas comment exécuter son nméquivalent.
o11c