Как встроить функцию только для сборки выпуска - PullRequest
3 голосов
/ 07 января 2011
// common.h
// This is foo function. It has a body.
__inline void foo() { /* something */ }

// a.cpp
#include "common.h" // for foo function
// Call foo

// b.cpp
#include "common.h" // for foo function
// Call foo

Я бы хотел включить функцию foo только при сборке для релиза. Я не хочу встроить функции для Debug сборки.

Я пробовал, но ошибки компоновщика раздражали меня.
В этом случае тело функции foo определяется в заголовочном файле common.h .
так что если я просто сделаю

//common.h
#if !defined(_DEBUG)
__inline
#endif
void foo() { /* something */ }

Будет встречена ошибка связи в DEBUG build. Потому что два модуля пытаются включить common.h.
Я понятия не имею, чтобы решить это.
Возможно ли это?

Ответы [ 6 ]

7 голосов
/ 07 января 2011

«Легким» решением будет следующее:

#if !defined(_DEBUG) || defined(NDEBUG)
#define INLINE inline
#else
#define INLINE static
#endif

static необходимо, чтобы заставить замолчать ошибки связывания и обойти правило единого определения.

Лучшим решением было бы просто отключить встраивание всего проекта для отладки. GCC поддерживает опции -wno-inline-functions и -fno-inline-small-functions для противодействия этим оптимизациям, а также не включает встраивание для -O1 или ниже (и, вероятно, -Os). Большинство компиляторов имеют похожие параметры.

Я называю последнее лучшим решением, потому что он должен дать указание компилятору игнорировать подсказку inline, устраняя необходимость в надоедливых директивах препроцессора.

5 голосов
/ 07 января 2011

Фундаментальная вещь, которую нужно осознать, заключается в том, что ключевое слово 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 в отладочных / неоптимизированных сборках?

4 голосов
/ 07 января 2011
#ifdef RELEASE
#define INLINE __inline
#else
#define INLINE
#endif

Тогда

// common.h
INLINE void foo() { /* something */ }

// a.cpp
#include "common.h" // for foo function
// Call foo

// b.cpp
#include "common.h" // for foo function
// Call foo

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

3 голосов
/ 07 января 2011

Вы не можете объявлять только функции заголовка как не встроенные.Вы можете объявить их как статические (или статические в стиле C), но это создаст копию функции (включая локальные статические переменные, если таковые имеются) в каждой единице перевода.Лучшее решение - оставить его как встроенный.В режиме отладки, когда оптимизация отключена, компиляторы обычно не включают какие-либо функции.

1 голос
/ 07 января 2011

В большинстве случаев флаги выпуска не определены.Обычно флаги отладки определены._DEBUG определяется большинством компиляторов, поэтому нет необходимости определять флаг выпуска:

Более практично:

#ifndef _DEBUG
    __inline void foo() { /* something */ }
#else 
     //some alternative
#endif
0 голосов
/ 07 января 2011

Используйте условия компилятора и определите флаг времени компиляции.

Пример:

#ifdef RELEASE_BUILD_FLAG
  //Run inline function
#else
  //some alternative
#endif
...