¿Qué hace extern inline?

93

Entiendo que inlineen sí mismo es una sugerencia para el compilador y, a su discreción, puede o no incluir la función en línea, y también producirá un código de objeto enlazable.

Creo que static inlinehace lo mismo (puede o no estar en línea) pero no producirá un código de objeto vinculable cuando esté en línea (ya que ningún otro módulo podría vincularlo).

¿Dónde extern inlineencaja en la imagen?

Suponga que quiero reemplazar una macro de preprocesador por una función en línea y requiero que esta función se inserte (por ejemplo, porque usa las macros __FILE__y __LINE__que deberían resolverse para la persona que llama pero no esta función llamada). Es decir, quiero ver un error del compilador o del enlazador en caso de que la función no se inserte. ¿Hace extern inlineesto? (Supongo que, si no es así, no hay forma de lograr este comportamiento más que seguir con una macro).

¿Existen diferencias entre C ++ y C?

¿Existen diferencias entre los diferentes proveedores y versiones de compiladores?

dragosht
fuente

Respuestas:

129

en K&R C o C89, inline no formaba parte del lenguaje. Muchos compiladores lo implementaron como una extensión, pero no había una semántica definida sobre cómo funcionaba. GCC fue de los primeros en implementar inlining, y se introdujo el inline, static inliney extern inlineconstrucciones; la mayoría de los compiladores anteriores a C99 generalmente siguen su ejemplo.

GNU89:

  • inline: la función puede estar en línea (aunque es solo una pista). Siempre se emite una versión fuera de línea y es visible desde el exterior. Por lo tanto, solo puede tener una línea definida de este tipo en una unidad de compilación, y todas las demás deben verla como una función fuera de línea (o obtendrá símbolos duplicados en el momento del enlace).
  • extern inline no generará una versión fuera de línea, pero puede llamar a una (que, por lo tanto, debe definir en alguna otra unidad de compilación. Sin embargo, se aplica la regla de una definición; la versión fuera de línea debe tener el mismo código que la inline que se ofrece aquí, en caso de que el compilador llame a eso.
  • static inlineno generará una versión fuera de línea visible externamente, aunque podría generar una versión estática de archivo. La regla de una definición no se aplica, ya que nunca se emite un símbolo externo ni se llama a uno.

C99 (o GNU99):

  • inline: como GNU89 "extern inline"; no se emite ninguna función visible externamente, pero se puede llamar a una y, por lo tanto, debe existir
  • extern inline: como GNU89 "en línea": se emite código visible externamente, por lo que como máximo una unidad de traducción puede usarlo.
  • static inline: como GNU89 "en línea estática". Este es el único portátil entre gnu89 y c99

C ++:

Una función que está en línea en cualquier lugar debe estar en línea en todas partes, con la misma definición. El compilador / enlazador clasificará varias instancias del símbolo. No existe una definición de static inlineo extern inline, aunque muchos compiladores las tienen (normalmente siguiendo el modelo gnu89).

puetzk
fuente
2
En Classic Classic C, "en línea" no era una palabra clave; estaba disponible para su uso como nombre de variable. Esto se aplicaría a C89 y pre-estándar (K&R) C.
Jonathan Leffler
Parece que tienes razón. Fijo. Pensé que se había reservado como palabra clave en C89 (aunque no en K&R), pero parece que lo
olvidé
Me gustaría agregar que para Visual C ++ de Microsoft, hay una palabra clave __forceinline que hará que su función se inserte. Obviamente, esta es una extensión específica del compilador solo para VC ++.
sin
¿Hay alguna diferencia entre C99 "extern inline" y ningún especificador?
Jo So
Semánticamente no; al igual que una función no en línea, extern inlineestá sujeta a la regla de una definición, y esta es la definición. Pero si las heurísticas de optimización definidas por la implementación siguen la recomendación de utilizar lainline palabra clave como una sugerencia de que "las llamadas a la función sean lo más rápidas posible" (ISO 9899: 1999 §6.7.4 (5), extern inlinecuenta
puetzk
31

Creo que malinterpretas __FILE__ y __LINE__ en base a esta declaración:

porque usa las macros __FILE__ y __LINE__ que deberían resolverse para la persona que llama pero no esta función llamada

Hay varias fases de compilación y el preprocesamiento es la primera. __FILE__ y __LINE__ se reemplazan durante esa fase. Entonces, para cuando el compilador pueda considerar la función para la inserción, ya habrá sido reemplazada.

Don Neufeld
fuente
14

Parece que estás intentando escribir algo como esto:

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

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

y con la esperanza de que se impriman valores diferentes cada vez. Como dice Don, no lo hará, porque el preprocesador implementa __FILE__ y __LINE__, pero el compilador implementa en línea. Entonces, desde donde llame a printLocation, obtendrá el mismo resultado.

La única forma de que esto funcione es hacer de printLocation una macro. (Sí, lo sé...)

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

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...
Roddy
fuente
18
Un truco común es que una macro PRINT_LOCATION llame a una función printLocation, pasando FILE y LINE como parámetros. Esto puede resultar en un mejor comportamiento del depurador / editor / etc.cuando el cuerpo de la función no es trivial.
Steve Jessop
@Roddy Mira mi solución, una extensión de la tuya, pero más completa y extensible.
enthusiasticgeek
@SteveJessop ¿Algo parecido a lo que he enumerado en la siguiente solución?
enthusiasticgeek
3

La situación con inline, static inline y extern inline es complicada, sobre todo porque gcc y C99 definen significados ligeramente diferentes para su comportamiento (y presumiblemente C ++ también). Puede encontrar información útil y detallada sobre lo que hacen en C aquí .

Simon Howard
fuente
2

Las macros son su elección aquí en lugar de las funciones en línea. Una rara ocasión en la que las macros dominan las funciones en línea. Intente lo siguiente: escribí este código "MACRO MAGIC" y debería funcionar. Probado en 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;
}

Para varios archivos, defina estas macros en un archivo de encabezado separado que incluya lo mismo en cada archivo c / cc / cxx / cpp. Siempre que sea posible, prefiera las funciones en línea o los identificadores constantes (según lo requiera el caso) sobre las macros.

entusiasta friki
fuente
2

En lugar de responder "¿qué hace?", Respondo "¿cómo hago para que haga lo que quiero?" Hay 5 tipos de inserción, todos disponibles en GNU C89, C99 estándar y C ++:

siempre en línea, a menos que se tome la dirección

Añadir __attribute__((always_inline)) a cualquier declaración, luego use uno de los casos siguientes para manejar la posibilidad de que se tome su dirección.

Probablemente nunca debería usar esto, a menos que necesite su semántica (por ejemplo, para afectar el ensamblaje de cierta manera, o para usar alloca ). El compilador generalmente sabe mejor que usted si vale la pena.

en línea y emitir un símbolo débil (como C ++, también conocido como "simplemente haz que funcione")

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

Tenga en cuenta que esto deja un montón de copias del mismo código por ahí, y el enlazador elige una arbitrariamente.

en línea, pero nunca emite ningún símbolo (dejando referencias externas)

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

emitir siempre (para una TU, para resolver lo anterior)

La versión insinuada emite un símbolo débil en C ++, pero un símbolo fuerte en cualquier dialecto de C:

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

O puede hacerlo sin la pista, que emite un símbolo fuerte en ambos idiomas:

void foo(void) { ... }

Por lo general, sabe en qué idioma es su TU cuando proporciona las definiciones y probablemente no necesite mucha inserción.

en línea y emitir en cada TU

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

Para todos estos, excepto staticuno, puede agregar una void foo(void)declaración arriba. Esto ayuda con la "mejor práctica" de escribir encabezados limpios y luego #includecrear un archivo separado con las definiciones en línea. Entonces, si usa líneas de estilo C,#define algunas macros de manera diferente en una TU dedicada para proporcionar las definiciones fuera de línea.

¡No olvide extern "C"si el encabezado se puede usar tanto desde C como desde C ++!

o11c
fuente
Falta: ¿qué pasa con MSVC? Tiene algunas extensiones de dialecto C89, pero nunca uso MSVC y no sé cómo ejecutar su nmequivalente.
o11c