Cosa fa l'extern inline?


93

Capisco che di inlineper sé è un suggerimento per il compilatore e, a sua discrezione, può o meno inline la funzione e produrrà anche codice oggetto collegabile.

Penso che static inlinefaccia lo stesso (può o non può inline) ma non produrrà codice oggetto collegabile quando inline (poiché nessun altro modulo potrebbe collegarsi ad esso).

Dove si extern inlineinserisce nell'immagine?

Supponiamo di voler sostituire una macro del preprocessore con una funzione inline e richiedere che questa funzione venga inline (ad esempio, perché utilizza le macro __FILE__e __LINE__che dovrebbero risolversi per il chiamante ma non questa funzione chiamata). Cioè, voglio vedere un errore del compilatore o del linker nel caso in cui la funzione non venga inline. Fa extern inlinequesto? (Presumo che, in caso contrario, non ci sia alcun modo per ottenere questo comportamento se non attenersi a una macro.)

Esistono differenze tra C ++ e C?

Esistono differenze tra diversi fornitori e versioni di compilatori?

Risposte:


129

in K&R C o C89, inline non faceva parte del linguaggio. Molti compilatori lo implementarono come estensione, ma non c'erano semantiche definite riguardo a come funzionava. GCC fu tra i primi ad attuare inlining, e introdotto il inline, static inlinee extern inlinecostrutti; la maggior parte dei compilatori pre-C99 generalmente segue il suo esempio.

GNU89:

  • inline: la funzione potrebbe essere inline (è solo un suggerimento però). Una versione fuori linea è sempre emessa e visibile esternamente. Quindi puoi avere un tale inline definito solo in un'unità di compilazione, e ogni altro deve vederlo come una funzione fuori linea (o otterrai simboli duplicati al momento del collegamento).
  • extern inline non genererà una versione fuori linea, ma potrebbe chiamarne una (che devi quindi definire in qualche altra unità di compilazione. Si applica la regola di una definizione, tuttavia; la versione fuori linea deve avere lo stesso codice della inline offerto qui, nel caso in cui il compilatore lo chiami.
  • static inlinenon genererà una versione fuori linea visibile esternamente, sebbene potrebbe generare un file statico. La regola della definizione unica non si applica, poiché non viene mai emesso un simbolo esterno né una chiamata a uno.

C99 (o GNU99):

  • inline: come GNU89 "extern inline"; non viene emessa alcuna funzione visibile esternamente, ma una potrebbe essere chiamata e quindi deve esistere
  • extern inline: come GNU89 "inline": viene emesso codice visibile esternamente, quindi al massimo un'unità di traduzione può usarlo.
  • static inline: come GNU89 "static inline". Questo è l'unico portatile tra gnu89 e c99

C ++:

Una funzione che è inline ovunque deve essere inline ovunque, con la stessa definizione. Il compilatore / linker risolverà più istanze del simbolo. Non esiste una definizione di static inlineo extern inline, sebbene molti compilatori li abbiano (tipicamente seguendo il modello gnu89).


2
Nella C classica classica, "inline" non era una parola chiave; era disponibile per l'uso come nome di variabile. Ciò si applicherebbe a C89 e pre-standard (K&R) C.
Jonathan Leffler

Hai ragione, a quanto pare. Fisso. Pensavo fosse stata riservata come parola chiave in C89 (anche se non in K&R), ma mi sembra di aver ricordato
male

Vorrei aggiungere che per Visual C ++ di Microsoft, c'è una parola chiave __forceinline che imporrà che la tua funzione venga inline. Questa è ovviamente un'estensione specifica del compilatore solo per VC ++.
senza

C'è qualche differenza tra C99 "extern inline" e nessun specificatore?
Jo So,

Semanticamente no; proprio come una funzione non inline, extern inlineè soggetta alla regola di una definizione, e questa è la definizione. Ma se l'euristica di ottimizzazione definita dall'implementazione segue la raccomandazione di utilizzare la inlineparola chiave come suggerimento che "le chiamate alla funzione siano il più veloci possibile" (ISO 9899: 1999 §6.7.4 (5), extern inlineconta
puetzk

31

Credo che tu fraintenda __FILE__ e __LINE__ sulla base di questa affermazione:

perché utilizza le macro __FILE__ e __LINE__ che dovrebbero risolversi per il chiamante ma non questa funzione chiamata

Ci sono diverse fasi di compilazione e la preelaborazione è la prima. __FILE__ e __LINE__ vengono sostituiti durante quella fase. Quindi, nel momento in cui il compilatore può considerare la funzione per l'inlining, sono già state sostituite.


14

Sembra che tu stia cercando di scrivere qualcosa del genere:

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

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

e sperando che ogni volta vengano stampati valori diversi. Come dice Don, non lo farai, perché __FILE__ e __LINE__ sono implementati dal preprocessore, ma inline è implementato dal compilatore. Quindi, ovunque tu chiami printLocation, otterrai lo stesso risultato.

L' unico modo per farlo funzionare è rendere printLocation una macro. (Si, lo so...)

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

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

18
Un trucco comune è che una macro PRINT_LOCATION chiami una funzione printLocation, passando FILE e LINE come parametri. Ciò può comportare un migliore comportamento del debugger / editor / ecc quando il corpo della funzione non è banale.
Steve Jessop,

@Roddy Guarda la mia soluzione: la tua estensione ma più completa ed estensibile.
entusiastico

@SteveJessop Qualcosa di simile ho elencato nella soluzione di seguito?
entusiastico

3

La situazione con inline, static inline ed extern inline è complicata, anche perché gcc e C99 definiscono significati leggermente diversi per il loro comportamento (e presumibilmente anche C ++). Puoi trovare alcune informazioni utili e dettagliate su ciò che fanno in C qui .


2

Le macro sono la tua scelta qui piuttosto che le funzioni inline. Una rara occasione in cui le macro dominano sulle funzioni inline. Prova quanto segue: ho scritto questo codice "MACRO MAGIC" e dovrebbe funzionare! Testato su 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;
}

Per più file, definire queste macro in un file di intestazione separato includendo lo stesso in ogni file c / cc / cxx / cpp. Si prega di preferire le funzioni inline o gli identificatori const (come richiesto dal caso) rispetto alle macro, ove possibile.


2

Invece di rispondere "cosa fa?", Rispondo "come faccio a fargli fare quello che voglio?" Esistono 5 tipi di inlining, tutti disponibili in GNU C89, standard C99 e C ++:

sempre in linea, a meno che non venga preso l'indirizzo

Aggiungere __attribute__((always_inline))a qualsiasi dichiarazione, quindi utilizzare uno dei seguenti casi per gestire la possibilità che il suo indirizzo venga preso.

Probabilmente non dovresti mai usarlo, a meno che tu non abbia bisogno della sua semantica (ad esempio per influenzare l'assembly in un certo modo, o per usarlo alloca). Il compilatore di solito sa meglio di te se ne vale la pena.

inline ed emette un simbolo debole (come C ++, anche noto come "fallo funzionare")

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

Si noti che questo lascia un mucchio di copie dello stesso codice in giro e il linker ne sceglie una arbitrariamente.

inline, ma non emette mai alcun simbolo (lasciando riferimenti esterni)

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

emetti sempre (per una TU, per risolvere la precedente)

La versione suggerita emette un simbolo debole in C ++, ma un simbolo forte in entrambi i dialetti di C:

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

Oppure puoi farlo senza il suggerimento, che emette un simbolo forte in entrambe le lingue:

void foo(void) { ... }

In generale, sai quale lingua è la tua TU quando fornisci le definizioni e probabilmente non hai bisogno di molto inlining.

inline ed emettono in ogni TU

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

Per tutti questi tranne staticuno, puoi aggiungere una void foo(void)dichiarazione sopra. Questo aiuta con la "best practice" di scrivere intestazioni pulite, quindi inserire #includeun file separato con le definizioni inline. Quindi, se si utilizza inline in stile C, #definealcune macro in modo diverso in una TU dedicata per fornire le definizioni fuori linea.

Non dimenticare extern "C"se l'intestazione potrebbe essere utilizzata sia da C che da C ++!


Mancante: che dire di MSVC? Ha alcune estensioni dialettali C89, ma non uso mai MSVC e non so come eseguirne l' nmequivalente.
o11c
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.