Apa yang dilakukan extern inline?

93

Saya memahami bahwa inlinedengan sendirinya adalah saran untuk kompiler, dan atas kebijakannya mungkin atau mungkin tidak sebaris fungsi, dan itu juga akan menghasilkan kode objek yang dapat ditautkan.

Saya pikir itu static inlinemelakukan hal yang sama (mungkin atau mungkin tidak sebaris) tetapi tidak akan menghasilkan kode objek yang dapat ditautkan ketika sebaris (karena tidak ada modul lain yang dapat menautkannya).

Di mana yang extern inlinecocok dengan gambar itu?

Asumsikan saya ingin mengganti makro preprocessor dengan fungsi inline dan mensyaratkan agar fungsi ini disisipkan (misalnya, karena menggunakan makro __FILE__dan __LINE__yang seharusnya menyelesaikan pemanggil tetapi bukan fungsi yang disebut ini). Artinya, saya ingin melihat kesalahan compiler atau linker jika fungsinya tidak inline. Apakah extern inlinemelakukan ini? (Saya berasumsi bahwa, jika tidak, tidak ada cara untuk mencapai perilaku ini selain tetap menggunakan makro.)

Apakah ada perbedaan antara C ++ dan C?

Apakah ada perbedaan antara vendor dan versi kompilator yang berbeda?

menyeret
sumber

Jawaban:

129

di K&R C atau C89, sebaris bukanlah bagian dari bahasa. Banyak kompiler yang menerapkannya sebagai ekstensi, tetapi tidak ada semantik yang ditentukan terkait cara kerjanya. GCC adalah orang yang pertama untuk menerapkan sebaris, dan memperkenalkan inline, static inlinedan extern inlinekonstruksi; kebanyakan compiler pra-C99 biasanya mengikuti petunjuknya.

GNU89:

  • inline: fungsi mungkin sebaris (ini hanya petunjuk). Versi out-of-line selalu dipancarkan dan terlihat secara eksternal. Karenanya Anda hanya dapat memiliki inline yang ditentukan dalam satu unit kompilasi, dan setiap unit lainnya perlu melihatnya sebagai fungsi out-of-line (atau Anda akan mendapatkan simbol duplikat pada waktu penautan).
  • extern inline tidak akan menghasilkan versi out-of-line, tetapi mungkin memanggil salah satu (yang oleh karena itu Anda harus mendefinisikan di beberapa unit kompilasi lain. Namun, aturan satu definisi berlaku; versi out-of-line harus memiliki kode yang sama dengan inline ditawarkan di sini, jika kompilator memanggilnya.
  • static inlinetidak akan menghasilkan versi out-of-line yang terlihat secara eksternal, meskipun itu mungkin menghasilkan file statis. Aturan satu definisi tidak berlaku, karena tidak pernah ada simbol eksternal yang dipancarkan atau panggilan ke salah satunya.

C99 (atau GNU99):

  • inline: seperti GNU89 "extern inline"; tidak ada fungsi yang terlihat secara eksternal yang dipancarkan, tetapi salah satunya mungkin dipanggil dan karena itu harus ada
  • extern inline: seperti GNU89 "inline": kode yang terlihat secara eksternal dipancarkan, jadi paling banyak satu unit terjemahan dapat menggunakan ini.
  • static inline: seperti GNU89 "static inline". Ini adalah satu-satunya perangkat portabel antara gnu89 dan c99

C ++:

Fungsi yang sebaris di mana pun harus sebaris di mana pun, dengan definisi yang sama. Compiler / linker akan memilah beberapa contoh simbol. Tidak ada definisi static inlineatau extern inline, meskipun banyak kompiler memilikinya (biasanya mengikuti model gnu89).

puetzk.dll
sumber
2
Dalam Klasik klasik C, 'inline' bukanlah kata kunci; itu tersedia untuk digunakan sebagai nama variabel. Ini akan berlaku untuk C89 dan pra-standar (K&R) C.
Jonathan Leffler
Sepertinya Anda benar. Tetap. Saya pikir itu telah dipesan sebagai kata kunci di C89 (meskipun tidak di K&R), tapi sepertinya saya salah ingat
puetzk
Saya ingin menambahkan itu untuk Microsoft Visual C ++, ada kata kunci __forceinline yang akan memaksa fungsi Anda menjadi inline. Ini jelas merupakan ekstensi khusus kompiler hanya untuk VC ++.
tanpa
Apakah ada perbedaan antara C99 "extern inline" dan tidak ada penentu sama sekali?
Jo Jadi
Secara semantik tidak; seperti fungsi non-inline, extern inlinetunduk pada aturan satu definisi, dan ini adalah definisi. Namun jika heuristik pengoptimalan yang ditentukan implementasi mengikuti saran untuk menggunakan inlinekata kunci sebagai saran bahwa "panggilan ke fungsi secepat mungkin" (ISO 9899: 1999 §6.7.4 (5), extern inlinehitungan
puetzk
31

Saya yakin Anda salah paham __FILE__ dan __LINE__ berdasarkan pernyataan ini:

karena menggunakan makro __FILE__ dan __LINE__ yang seharusnya menyelesaikan pemanggil tetapi bukan fungsi yang disebut ini

Ada beberapa fase kompilasi, dan preprocessing adalah yang pertama. __FILE__ dan __LINE__ diganti selama fase itu. Jadi pada saat kompilator dapat mempertimbangkan fungsi untuk sebaris, mereka telah diganti.

Don Neufeld
sumber
14

Sepertinya Anda mencoba menulis sesuatu seperti ini:

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

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

dan berharap Anda akan mendapatkan nilai yang berbeda setiap kali dicetak. Seperti yang dikatakan Don, Anda tidak akan melakukannya, karena __FILE__ dan __LINE__ diimplementasikan oleh preprocessor, tetapi inline diimplementasikan oleh compiler. Jadi, di mana pun Anda memanggil printLocation from, Anda akan mendapatkan hasil yang sama.

Satu- satunya cara Anda bisa membuatnya bekerja adalah dengan membuat printLocation makro. (Ya saya tahu...)

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

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...
Roddy
sumber
18
Trik yang umum adalah makro PRINT_LOCATION memanggil fungsi printLocation, meneruskan FILE dan LINE sebagai parameter. Hal ini dapat menghasilkan perilaku debugger / editor / etc yang lebih baik ketika badan fungsi tidak sepele.
Steve Jessop
@Roddy Lihatlah solusi saya - perluasan milik Anda tetapi lebih komprehensif dan dapat diperluas.
enthusiasticgeek
@SteveJessop Sesuatu seperti saya telah tercantum dalam solusi di bawah ini?
enthusiasticgeek
3

Situasi dengan sebaris, sebaris statis, dan sebaris eksternal rumit, paling tidak karena gcc dan C99 mendefinisikan arti yang sedikit berbeda untuk perilakunya (dan mungkin juga C ++). Anda dapat menemukan beberapa informasi berguna dan rinci tentang apa yang mereka lakukan di C di sini .

Simon Howard
sumber
2

Makro adalah pilihan Anda di sini daripada fungsi sebaris. Kejadian langka saat makro menguasai fungsi sebaris. Coba yang berikut ini: Saya menulis kode "MACRO MAGIC" ini dan seharusnya berhasil! Diuji di 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;
}

Untuk beberapa file, tentukan makro ini dalam file header terpisah termasuk yang sama di setiap file c / cc / cxx / cpp. Harap lebih memilih fungsi sebaris atau pengenal konstanta (sesuai tuntutan kasus) daripada makro jika memungkinkan.

enthusiasticgeek
sumber
2

Alih-alih menjawab "apa fungsinya?", Saya menjawab "bagaimana cara membuatnya melakukan apa yang saya inginkan?" Ada 5 jenis sebaris, semuanya tersedia di GNU C89, C99 standar, dan C ++:

selalu sebaris, kecuali ada alamat yang diambil

Menambahkan __attribute__((always_inline)) ke deklarasi apa pun, lalu gunakan salah satu kasus di bawah ini untuk menangani kemungkinan alamatnya diambil.

Anda mungkin tidak boleh menggunakan ini, kecuali jika Anda membutuhkan semantiknya (misalnya untuk mempengaruhi assembly dengan cara tertentu, atau menggunakan alloca ). Kompiler biasanya lebih tahu dari Anda apakah itu sepadan.

sebaris dan memancarkan simbol yang lemah (seperti C ++, alias "buat saja berfungsi")

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

Perhatikan bahwa ini meninggalkan banyak salinan dari kode yang sama tergeletak di sekitar, dan linker mengambilnya secara sewenang-wenang.

sebaris, tetapi tidak pernah memancarkan simbol apa pun (meninggalkan referensi eksternal)

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

memancarkan selalu (untuk satu TU, untuk menyelesaikan yang sebelumnya)

Versi yang diisyaratkan memancarkan simbol yang lemah di C ++, tetapi simbol yang kuat di kedua dialek C:

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

Atau Anda dapat melakukannya tanpa petunjuk, yang memancarkan simbol yang kuat dalam kedua bahasa:

void foo(void) { ... }

Umumnya, Anda tahu bahasa apa TU Anda saat memberikan definisi, dan mungkin tidak perlu banyak penyebarisan.

sebaris dan memancarkan di setiap TU

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

Untuk semua ini kecuali staticsatu, Anda dapat menambahkan void foo(void)deklarasi di atas. Ini membantu dengan "praktik terbaik" dalam menulis header yang bersih, kemudian #includemembuat file terpisah dengan definisi sebaris. Kemudian, jika menggunakan sebaris gaya C,#define beberapa makro berbeda dalam satu TU khusus untuk memberikan definisi di luar baris.

Jangan lupa extern "C"jika header mungkin digunakan dari C dan C ++!

o11c
sumber
Hilang: bagaimana dengan MSVC? Ia memiliki beberapa ekstensi dialek C89, tetapi saya tidak pernah menggunakan MSVC dan tidak tahu bagaimana menjalankan nmpadanannya.
o11c