Внешняя линия

Я понимаю, что "встроенный" сам по себе является предложением для компилятора, и по своему описанию он может или не может встроить функцию, а также будет создавать связываемый объектный код.

Я думаю, что "статический inline" делает то же самое (может или не является встроенным), но не будет создавать ссылочный объектный код при вставке (так как ни один другой модуль не может ссылаться на него).

Где "extern inline" вписывается в изображение?

Предположим, что я хочу заменить макрос препроцессора встроенной функцией и потребовать, чтобы эта функция была встроена (например, потому что она использует макросы __FILE__ и __LINE__, которые должны быть разрешены для вызывающего, но не эта вызываемая функция). То есть, я хочу увидеть ошибку компилятора или компоновщика в случае, если функция не будет вставлена. Выполняет ли это "extern inline"? (Я предполагаю, что, если это не так, невозможно достичь такого поведения, кроме как придерживаться макроса.)

Существуют ли различия между С++ и C?

Существуют ли различия между разными поставщиками компилятора и версиями?

Ответ 1

в K & R C или C89, inline не был частью языка. Многие компиляторы реализовали его как расширение, но не было определенной семантики относительно того, как это работает. GCC был одним из первых, кто реализовал inlining, и представил конструкции inline, static inline и extern inline; большинство компиляторов pre-C99 обычно следуют за ним.

GNU89:

  • inline: функция может быть встроена (это всего лишь подсказка). Исключительная версия всегда излучается и внешне видима. Следовательно, вы можете иметь только такую ​​встроенную строку, определенную в одном блоке компиляции, и каждый другой должен видеть ее как функцию вне линии (или вы получите дубликаты символов во время связи).
  • extern inline не будет генерировать версию вне очереди, но может вызывать одно (которое вы должны определить в каком-то другом компиляторе). Тем не менее, применяется правило одного определения, а внешняя версия должна имеют тот же код, что и встроенный inline, в случае, если вместо этого компилятор называет это.
  • static inline не будет генерировать внешнюю видимую версию вне очереди, хотя она может генерировать статический файл. Правило с одним определением не применяется, так как никогда не издаётся внешний символ или вызов.

C99 (или GNU99):

  • inline: как GNU89 "extern inline"; никакая внешняя видимая функция не испускается, но ее можно вызвать и, следовательно, должны существовать.
  • extern inline: как GNU89 "inline": выдается видимый извне код, поэтому самое большее может использовать это устройство перевода.
  • static inline: как GNU89 "статический встроенный". Это единственная портативная между gnu89 и c99

С++:

Функция, которая должна быть встроена в любом месте, должна быть построена повсюду, с тем же определением. Компилятор/компоновщик будет сортировать несколько экземпляров символа. Не существует определения static inline или extern inline, хотя многие компиляторы имеют их (как правило, после модели gnu89).

Ответ 2

Я считаю, что вы неправильно понимаете __FILE__ и __LINE__ на основе этого утверждения:

поскольку использует __FILE__ и Макросы __LINE__, которые должны быть разрешены для вызывающего, но не вызываемые Функция

Существует несколько этапов компиляции, и предварительная обработка является первой. __FILE__ и __LINE__ заменяются на этом этапе. Поэтому к тому моменту, когда компилятор может рассмотреть функцию для inlining, они уже были заменены.

Ответ 3

Похоже, вы пытаетесь написать что-то вроде этого:

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

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

и надеемся, что вы будете получать разные значения, напечатанные каждый раз. Как говорит Дон, вы не будете, потому что __FILE__ и __LINE__ реализованы препроцессором, но встроенный реализован компилятором. Поэтому везде, где вы вызываете printLocation, вы получите тот же результат.

только, чтобы вы могли заставить это работать, - сделать printLocation макросом. (Да, я знаю...)

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

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

Ответ 4

Ситуация с встроенными, статическими встроенными и внешними inline сложна, не в последнюю очередь потому, что gcc и C99 определяют несколько разные значения для их поведения (и, предположительно, С++). Вы можете найти полезную и подробную информацию о том, что они делают в C здесь.

Ответ 5

Макросы - ваш выбор здесь, а не встроенные функции. Редкий случай, когда макросы управляют встроенными функциями. Попробуйте следующее: я написал этот код "MACRO MAGIC", и он должен работать! Протестировано на 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;
}

Для нескольких файлов эти макросы определяют в отдельном файле заголовка, в том числе в каждом файле c/cc/cxx/cpp. Пожалуйста, предпочитайте встроенные функции или константные идентификаторы (в зависимости от случая) над макросами где это возможно.

Ответ 6

Вместо того, чтобы отвечать "что он делает?", Я отвечаю "как заставить его делать то, что я хочу?" Существует 5 видов встраивания, все они доступны в GNU C89, стандарте C99 и C++:

всегда в строке, если адрес не занят

Добавьте __attribute__((always_inline)) к любому объявлению, а затем используйте один из следующих случаев, чтобы обработать возможность получения его адреса.

Вы, вероятно, никогда не должны использовать это, если вам не нужна его семантика (например, чтобы повлиять на сборку определенным образом или использовать alloca). Компилятор обычно лучше вас знает, стоит ли это того.

встроенный и испускающий слабый символ (например, C++, он же "просто заставь это работать")

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

Обратите внимание, что это оставляет кучу копий одного и того же кода, а компоновщик выбирает один произвольно.

встроенный, но никогда не испускает никакого символа (оставляя внешние ссылки)

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

выбрасывать всегда (для одного TU, для разрешения предыдущего)

Подсказка выдает слабый символ в C++, но сильный символ на любом из диалектов C:

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

Или вы можете сделать это без подсказки, которая испускает сильный символ на обоих языках:

void foo(void) { ... }

Как правило, вы знаете, на каком языке ваш TU, когда вы предоставляете определения, и, вероятно, вам не нужно много встраивать.

встроенный и излучающий в каждом ТУ

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

Для всего этого, кроме static, вы можете добавить объявление void foo(void) выше. Это помогает с "лучшей практикой" написания чистых заголовков, а затем #include отдельный файл со встроенными определениями. Затем, если используются строки в стиле C, #define разному определяет макрос в одном выделенном TU, чтобы обеспечить определения вне строки.

Не забудьте extern "C" если заголовок может использоваться как из C, так и из C++!