Was macht extern inline?

93

Ich verstehe, dass dies inlinefür sich genommen ein Vorschlag für den Compiler ist und nach eigenem Ermessen die Funktion einbinden kann oder nicht, und dass er auch verknüpfbaren Objektcode erzeugt.

Ich denke, dass static inlinedies dasselbe tut (kann oder kann nicht inline sein), aber keinen verlinkbaren Objektcode erzeugt, wenn es inline ist (da kein anderes Modul darauf verlinken könnte).

Wo extern inlinepasst das ins Bild?

Angenommen , ich eine Präprozessormakro durch eine Inline - Funktion ersetzt werden soll und verlangen , dass diese Funktion inlined wird ( zum Beispiel , weil es die verwendet __FILE__und __LINE__Makros , die für die Anrufer lösen sollen aber nicht so genannt Funktion). Das heißt, ich möchte einen Compiler- oder Linkerfehler sehen, falls die Funktion nicht inline wird. Tut extern inlinedas? (Ich gehe davon aus, dass es keine andere Möglichkeit gibt, dieses Verhalten zu erreichen, als sich an ein Makro zu halten.)

Gibt es Unterschiede zwischen C ++ und C?

Gibt es Unterschiede zwischen verschiedenen Compiler-Anbietern und -Versionen?

Dragosht
quelle

Antworten:

129

In K & R C oder C89 war Inline nicht Teil der Sprache. Viele Compiler haben es als Erweiterung implementiert, aber es gab keine definierte Semantik bezüglich der Funktionsweise. GCC war unter den ersten inlining zu implementieren, und führte das inline, static inlineund extern inlineKonstrukte; Die meisten Pre-C99-Compiler folgen im Allgemeinen ihrem Beispiel.

GNU89:

  • inline: Die Funktion ist möglicherweise inline (dies ist jedoch nur ein Hinweis). Eine Offline-Version wird immer ausgegeben und ist von außen sichtbar. Daher kann eine solche Inline nur in einer Kompilierungseinheit definiert werden, und jede andere muss sie als Out-of-Line-Funktion betrachten (oder Sie erhalten zum Zeitpunkt der Verknüpfung doppelte Symbole).
  • extern inline generiert keine Out-of-Line-Version, ruft jedoch möglicherweise eine auf (die Sie daher in einer anderen Kompilierungseinheit definieren müssen. Es gilt jedoch die One-Definition-Regel; die Out-of-Line-Version muss denselben Code wie die haben Inline wird hier angeboten, falls der Compiler dies stattdessen aufruft.
  • static inlinegeneriert keine extern sichtbare Offline-Version, obwohl möglicherweise eine statische Datei generiert wird. Die Ein-Definitions-Regel gilt nicht, da weder ein externes Symbol ausgegeben noch ein Symbol aufgerufen wird.

C99 (oder GNU99):

  • inline: wie GNU89 "extern inline"; Es wird keine extern sichtbare Funktion ausgegeben, aber eine könnte aufgerufen werden und muss daher existieren
  • extern inline: wie GNU89 "inline": Es wird extern sichtbarer Code ausgegeben, sodass höchstens eine Übersetzungseinheit diesen verwenden kann.
  • static inline: wie GNU89 "statische Inline". Dies ist das einzige tragbare Gerät zwischen gnu89 und c99

C ++:

Eine Funktion, die überall inline ist, muss überall inline sein und dieselbe Definition haben. Der Compiler / Linker sortiert mehrere Instanzen des Symbols. Es gibt keine Definition von static inlineoder extern inline, obwohl viele Compiler sie haben (normalerweise nach dem gnu89-Modell).

Puetzk
quelle
2
In Classic Classic C war "Inline" kein Schlüsselwort. Es war als Variablenname verfügbar. Dies würde für C89 und Pre-Standard (K & R) C gelten.
Jonathan Leffler
Sie haben Recht, wie es scheint. Fest. Ich dachte, es sei als Schlüsselwort in C89 reserviert worden (wenn auch nicht in K & R), aber ich glaube, ich habe mich falsch erinnert
Puetzk
Ich möchte hinzufügen, dass es für Microsoft Visual C ++ ein __forceinline-Schlüsselwort gibt, das das Inlinen Ihrer Funktion erzwingt. Dies ist offensichtlich eine compilerspezifische Erweiterung nur für VC ++.
untitled8468927
Gibt es einen Unterschied zwischen C99 "extern inline" und überhaupt keinen Spezifizierern?
Jo So
Semantisch nein; Genau wie eine Nicht-Inline-Funktion extern inlineunterliegt sie der Ein-Definitions-Regel, und dies ist die Definition. Wenn jedoch die implementierungsdefinierten Optimierungsheuristiken der Empfehlung folgen, das inlineSchlüsselwort als Vorschlag zu verwenden, dass "Aufrufe der Funktion so schnell wie möglich sein sollen" (ISO 9899: 1999 §6.7.4 (5), extern inlinezählt
puetzk
31

Ich glaube, Sie verstehen __FILE__ und __LINE__ aufgrund dieser Aussage falsch:

weil es die Makros __FILE__ und __LINE__ verwendet, die für den Aufrufer aufgelöst werden sollen, aber nicht diese aufgerufene Funktion

Es gibt mehrere Phasen der Kompilierung, und die Vorverarbeitung ist die erste. __FILE__ und __LINE__ werden in dieser Phase ersetzt. Bis der Compiler die Funktion zum Inlining in Betracht ziehen kann, wurden sie bereits ersetzt.

Don Neufeld
quelle
14

Es hört sich so an, als würden Sie versuchen, so etwas zu schreiben:

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

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

und in der Hoffnung, dass jedes Mal andere Werte gedruckt werden. Wie Don sagt, werden Sie nicht, weil __FILE__ und __LINE__ vom Präprozessor implementiert werden, aber Inline vom Compiler implementiert wird. Wo immer Sie printLocation aufrufen, erhalten Sie das gleiche Ergebnis.

Die einzige Möglichkeit, dies zum Laufen zu bringen, besteht darin, printLocation zu einem Makro zu machen. (Ja, ich weiß...)

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

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...
Roddy
quelle
18
Ein häufiger Trick besteht darin, dass ein Makro PRINT_LOCATION eine Funktion printLocation aufruft und FILE und LINE als Parameter übergibt . Dies kann zu einem besseren Verhalten von Debugger / Editor / etc führen, wenn der Funktionskörper nicht trivial ist.
Steve Jessop
@Roddy Schau dir meine Lösung an - Erweiterung von dir, aber umfassender und erweiterbarer.
Enthusiastgeek
@SteveJessop So etwas habe ich in der folgenden Lösung aufgelistet?
Enthusiastgeek
3

Die Situation mit Inline, statischem Inline und externem Inline ist kompliziert, nicht zuletzt, weil gcc und C99 leicht unterschiedliche Bedeutungen für ihr Verhalten definieren (und vermutlich auch C ++). Sie können einige nützliche und detaillierte Informationen finden , was sie in C tun hier .

Simon Howard
quelle
2

Hier wählen Sie eher Makros als Inline-Funktionen. Eine seltene Gelegenheit, in der Makros über Inline-Funktionen herrschen. Versuchen Sie Folgendes: Ich habe diesen "MACRO MAGIC" -Code geschrieben und er sollte funktionieren! Getestet auf 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;
}

Definieren Sie diese Makros für mehrere Dateien in einer separaten Header-Datei, einschließlich derselben in jeder c / cc / cxx / cpp-Datei. Bitte bevorzugen Sie nach Möglichkeit Inline-Funktionen oder konstante Bezeichner (je nach Fall) gegenüber Makros.

enthusiastisch
quelle
2

Anstatt zu antworten "Was macht es?", Antworte ich "Wie bringe ich es dazu, das zu tun, was ich will?" Es gibt 5 Arten von Inlining, die alle in GNU C89, Standard C99 und C ++ verfügbar sind:

immer inline, es sei denn, die Adresse ist vergeben

Fügen Sie __attribute__((always_inline))eine Erklärung hinzu und verwenden Sie dann einen der folgenden Fälle, um die Möglichkeit zu prüfen, dass die Adresse verwendet wird.

Sie sollten dies wahrscheinlich niemals verwenden, es sei denn, Sie benötigen seine Semantik (z. B. um die Assembly auf eine bestimmte Weise zu beeinflussen oder um sie zu verwenden alloca). Der Compiler weiß normalerweise besser als Sie, ob es sich lohnt.

Inline und ein schwaches Symbol ausgeben (wie C ++, auch bekannt als "Lass es einfach funktionieren")

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

Beachten Sie, dass dadurch eine Reihe von Kopien desselben Codes herumliegen und der Linker eine willkürlich auswählt.

Inline, aber niemals ein Symbol ausgeben (externe Referenzen hinterlassen)

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

immer ausgeben (für eine TU, um das vorhergehende aufzulösen)

Die angedeutete Version gibt ein schwaches Symbol in C ++ aus, aber ein starkes Symbol in beiden Dialekten von C:

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

Oder Sie können es ohne den Hinweis tun, der in beiden Sprachen ein starkes Symbol ausgibt:

void foo(void) { ... }

Im Allgemeinen wissen Sie, welche Sprache Ihre TU ist, wenn Sie die Definitionen bereitstellen, und benötigen wahrscheinlich nicht viel Inlining.

Inline und emittieren in jeder TU

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

Für alle diese außer der staticeinen können Sie void foo(void)oben eine Erklärung hinzufügen . Dies hilft bei der "Best Practice", saubere Header zu schreiben und dann #includeeine separate Datei mit den Inline-Definitionen zu erstellen. Wenn Sie dann Inlines im C-Stil verwenden, unterscheiden sich #defineeinige Makros in einer dedizierten TU, um die Out-of-Line-Definitionen bereitzustellen.

Vergessen Sie nicht, extern "C"ob der Header sowohl von C als auch von C ++ verwendet werden kann!

o11c
quelle
Vermisst: Was ist mit MSVC? Es hat einige C89-Dialekterweiterungen, aber ich verwende nie MSVC und weiß nicht, wie ich das nmÄquivalent ausführen soll .
o11c