O que extern inline faz?


93

Eu entendo que inlinepor si só é uma sugestão para o compilador e, a seu critério, ele pode ou não embutir a função e também produzir código de objeto vinculável.

Acho que static inlinefaz o mesmo (pode ou não embutir), mas não produzirá código de objeto vinculável quando embutido (já que nenhum outro módulo pode ser vinculado a ele).

Onde se extern inlineencaixa na imagem?

Suponha que eu queira substituir uma macro de pré-processador por uma função embutida e exigir que essa função seja embutida (por exemplo, porque ela usa as macros __FILE__e __LINE__que devem ser resolvidas para o chamador, mas não para esta função chamada). Ou seja, desejo ver um erro do compilador ou do vinculador no caso de a função não ficar embutida. Faz extern inlineisso? (Presumo que, se não, não há outra maneira de atingir esse comportamento além de manter uma macro.)

Existem diferenças entre C ++ e C?

Existem diferenças entre os diferentes fornecedores e versões de compiladores?

Respostas:


129

em K&R C ou C89, inline não fazia parte da linguagem. Muitos compiladores o implementaram como uma extensão, mas não havia uma semântica definida sobre como funcionava. GCC foi um dos primeiros a implementar inlining, e introduziu o inline, static inlinee extern inlineconstruções; a maioria dos compiladores pré-C99 geralmente segue seu exemplo.

GNU89:

  • inline: a função pode estar embutida (embora seja apenas uma dica). Uma versão fora de linha é sempre emitida e visível externamente. Portanto, você só pode ter tal inline definido em uma unidade de compilação, e todas as outras precisam vê-lo como uma função fora de linha (ou você obterá símbolos duplicados no momento do link).
  • extern inline não irá gerar uma versão fora de linha, mas pode chamar uma (que você deve definir em alguma outra unidade de compilação. A regra de uma definição se aplica, entretanto; a versão fora de linha deve ter o mesmo código que o inline oferecido aqui, no caso de o compilador chamar isso.
  • static inlinenão irá gerar uma versão fora de linha visível externamente, embora possa gerar uma versão estática de arquivo. A regra de uma definição não se aplica, uma vez que nunca há um símbolo externo emitido nem uma chamada para um.

C99 (ou GNU99):

  • inline: como GNU89 "extern inline"; nenhuma função visível externamente é emitida, mas uma pode ser chamada e, portanto, deve existir
  • extern inline: como GNU89 "inline": código visível externamente é emitido, portanto, no máximo uma unidade de tradução pode usá-lo.
  • static inline: como GNU89 "inline estático". Este é o único portátil entre gnu89 e c99

C ++:

Uma função que está embutida em qualquer lugar deve estar embutida em qualquer lugar, com a mesma definição. O compilador / vinculador irá classificar várias instâncias do símbolo. Não há definição de static inlineou extern inline, embora muitos compiladores os tenham (normalmente seguindo o modelo gnu89).


2
Em Classic classic C, 'inline' não era uma palavra-chave; estava disponível para uso como um nome de variável. Isso se aplicaria ao C89 e ao pré-padrão (K&R) C.
Jonathan Leffler

Você está correto, ao que parece. Fixo. Achei que tivesse sido reservada como uma palavra-chave em C89 (embora não em K&R), mas parece que me
lembrei mal

Eu gostaria de adicionar que para o Visual C ++ da Microsoft, há uma palavra-chave __forceinline que forçará a sua função ficar embutida. Esta é obviamente uma extensão específica do compilador apenas para VC ++.
untitled8468927

Existe alguma diferença entre C99 "extern inline" e nenhum especificador?
Jo So

Semanticamente não; assim como uma função não embutida, extern inlineestá sujeita a uma regra de definição e esta é a definição. Mas se as heurísticas de otimização definidas pela implementação seguirem a recomendação de usar a inlinepalavra - chave como uma sugestão de que "as chamadas para a função sejam o mais rápido possível" (ISO 9899: 1999 §6.7.4 (5), extern inlinecontagens
puetzk

31

Eu acredito que você interpretou mal __FILE__ e __LINE__ com base nesta declaração:

porque ele usa as macros __FILE__ e __LINE__ que devem resolver para o chamador, mas não esta função chamada

Existem várias fases de compilação e o pré-processamento é a primeira. __FILE__ e __LINE__ são substituídos durante essa fase. Portanto, no momento em que o compilador pode considerar a função para inlining, eles já foram substituídos.


14

Parece que você está tentando escrever algo assim:

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

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

e esperando que você obtenha valores diferentes impressos a cada vez. Como Don diz, você não vai, porque __FILE__ e __LINE__ são implementados pelo pré-processador, mas em linha é implementado pelo compilador. Portanto, de onde quer que você chame printLocation, você obterá o mesmo resultado.

A única maneira de fazer isso funcionar é transformar printLocation em uma macro. (Sim eu conheço...)

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

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

18
Um truque comum é uma macro PRINT_LOCATION chamar uma função printLocation, passando FILE e LINE como parâmetros. Isso pode resultar em um comportamento melhor do depurador / editor / etc, quando o corpo da função não é trivial.
Steve Jessop

@Roddy Olhe para a minha solução - extensão sua, mas mais abrangente e extensível.
entusiasticgeek

@SteveJessop Algo como eu listei na solução abaixo?
entusiasticgeek

3

A situação com inline, inline estático e inline externo é complicada, até porque gcc e C99 definem significados ligeiramente diferentes para seu comportamento (e presumivelmente C ++, também). Você pode encontrar algumas informações úteis e detalhadas sobre o que eles fazem em C aqui .


2

As macros são sua escolha aqui, em vez das funções embutidas. Uma ocasião rara em que as macros dominam as funções embutidas. Tente o seguinte: Escrevi este código "MACRO MAGIC" e deve funcionar! Testado em 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 vários arquivos, defina essas macros em um arquivo de cabeçalho separado, incluindo o mesmo em cada arquivo c / cc / cxx / cpp. Prefira funções inline ou identificadores const (conforme o caso) em vez de macros sempre que possível.


2

Em vez de responder "o que isso faz?", Estou respondendo "como faço para fazer o que quero?" Existem 5 tipos de inlining, todos disponíveis em GNU C89, C99 padrão e C ++:

sempre inline, a menos que o endereço seja obtido

Adicione __attribute__((always_inline))a qualquer declaração e use um dos casos abaixo para lidar com a possibilidade de seu endereço ser tomado.

Você provavelmente nunca deve usar isso, a menos que precise de sua semântica (por exemplo, para afetar o assembly de uma determinada maneira ou para usar alloca). O compilador geralmente sabe melhor do que você se vale a pena.

embutir e emitir um símbolo fraco (como C ++, também conhecido como "apenas faça funcionar")

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

Observe que isso deixa um monte de cópias do mesmo código por aí e o vinculador escolhe uma arbitrariamente.

inline, mas nunca emite nenhum símbolo (deixando referências externas)

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

emitir sempre (para um TU, para resolver o anterior)

A versão sugerida emite um símbolo fraco em C ++, mas um símbolo forte em qualquer dialeto de C:

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

Ou você pode fazer isso sem a dica, que emite um símbolo forte em ambos os idiomas:

void foo(void) { ... }

Geralmente, você sabe qual é o idioma do seu TU ao fornecer as definições e provavelmente não precisa de muitos inlining.

inline e emitir em cada TU

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

Para todos eles, exceto staticum, você pode adicionar uma void foo(void)declaração acima. Isso ajuda na "prática recomendada" de escrever cabeçalhos limpos e, em seguida, #includeinserir um arquivo separado com as definições embutidas. Então, se estiver usando sequências de estilo C, #definealguma macro de maneira diferente em uma TU dedicada para fornecer as definições fora de linha.

Não se esqueça extern "C"se o cabeçalho pode ser usado tanto em C quanto em C ++!


Em falta: e quanto ao MSVC? Tem algumas extensões de dialeto C89, mas nunca uso MSVC e não sei como rodar seu nmequivalente.
11c
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.