Фундаментальная вещь, которую нужно осознать, заключается в том, что ключевое слово inline
(или расширение __inline
от Microsoft для C - поскольку MSVC не поддерживает C99) по сути является проходом, нарушающим правило одного определения. Если вы думаете об этом - это все, что есть на самом деле, поскольку компилятор не обязан фактически выполнять какие-либо вставки.
Итак, когда у вас есть функция inline
, вам разрешено иметь функцию, определенную более чем в одном модуле. Фактически, вы обязаны определить его в любом модуле, который фактически использует функцию.
Однако, если вы не объявляете функцию как inline
, вы должны убедиться, что у вас есть не более одного определения (ровно одно, если оно действительно используется). Для функций, не являющихся членами (все функции в C), существует несколько способов:
- объявите функцию как
static
, чтобы изменить ее связь с внутренней (обратите внимание, что вы можете иметь static inline
функции для начала).
- в C ++ вы можете поместить их в анонимное пространство имен (что похоже на объявление статического)
- вы можете использовать препроцессор для манипулирования этим. Это некрасиво, но это работает, и я видел технику, успешно используемую в дикой природе. Стоит ли затраченных усилий - это совсем другое - вам придется решать это самостоятельно.
По сути, вам нужно реализовать функцию в отдельном файле .c, точно так же, как если бы вы следовали традиции стандарта кодирования «одна функция на модуль» (фактически вы можете сделать это так же хорошо, если поместить несколько встроенных функций в модуль .c - но все они должны быть встроенными или не встроенными как группа, чтобы не дать вещам выйти из-под контроля). Реализация функции должна быть организована так, чтобы ее можно было включить в заголовок, поэтому она должна включать охрану, как и любой другой заголовок. Затем вы используете препроцессор для условного включения реализации в качестве части заголовка, когда вам нужны встроенные функции (так что реализация будет доступна для всех модулей), но не включайте ее, если вы не встраиваете (поэтому вы следуете определение правила в этом случае):
Заголовок common.h
:
// common.h
#ifndef COMMON_H
#define COMMON_H
#ifdef RELEASE
#define USE_INLINE
#define INLINE __inline
#else
#define INLINE
#endif
INLINE void foo(void);
#ifdef USE_INLINE
#include "foo.c"
#endif
#endif /* COMMON_H */
Реализация foo()
:
// foo.c
#ifndef FOO_C
#define FOO_C
#include <stdio.h>
#include "common.h"
INLINE void foo()
{
printf("foo\n");
}
#endif /* FOO_C */
И пример программы:
// main.c
#include<stdio.h>
#include "common.h"
int main()
{
foo();
return 0;
}
Теперь, если вы скомпилируете для релиза:
cl /DRELEASE main.c foo.c
foo()
будет inline
(или __inline
в зависимости от обстоятельств).
Если вы компилируете для не-релиза:
cl test.c foo.c
у вас есть не встроенный foo()
.
И компилятор, и компоновщик счастливы в любом случае.
С учетом всего вышесказанного мне нравится предложение переопределить INLINE
на static
для целей отладки.
Однако, в конечном счете, я не уверен, что вижу смысл в этом - современные отладчики могут выполнять пошаговые действия с функциями inline
, и отладчик, вероятно, не будет выполнять встроенные вызовы функций, если вы отключите оптимизацию. Таким образом, вы можете установить точки останова и внутри встроенной функции, и она будет работать нормально в неоптимизированных сборках.
Я не совсем уверен, что вы конечной цели в этом на самом деле. Какой недостаток заключается в том, чтобы оставить функции как inline
в отладочных / неоптимизированных сборках?