внешняя линия - PullRequest
       31

внешняя линия

85 голосов
/ 19 октября 2008

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

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

Где "внешняя линия" вписывается в картину?

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

Есть ли различия между C ++ и C?

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

Ответы [ 6 ]

122 голосов
/ 19 октября 2008

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

GNU89:

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

C99 (или GNU99):

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

C ++:

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

29 голосов
/ 19 октября 2008

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

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

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

12 голосов
/ 20 октября 2008

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

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;
...
3 голосов
/ 20 октября 2008

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

2 голосов
/ 07 августа 2012

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

0 голосов
/ 08 июля 2018

Вместо ответа «что он делает?» Я отвечаю «как заставить его делать то, что я хочу?» Существует 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 ++!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...