it-swarm.com.ru

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

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

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

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

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

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

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

83
wilbur_m

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

GNU89:

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

C99 (или GNU99):

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

C++:

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

119
puetzk

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

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

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

29
Don Neufeld

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

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

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

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

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

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

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...
12
Roddy

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

3
Simon Howard

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

2
enthusiasticgeek

Вместо ответа «что он делает?» Я отвечаю «как заставить его делать то, что я хочу?» Существует 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) выше. Это помогает с «лучшей практикой» написания чистых заголовков, а затем #includeing отдельного файла со встроенными определениями. Затем, если используются строчные символы в стиле C, #define по-разному используется в одном выделенном TU макросе, чтобы обеспечить определения вне строки.

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

0
o11c