Повторное определение подставляемых функций при соединении статичный освобождает

У меня есть программа C++, которую я компилирую с mingw (gcc для Windows). Используя выпуск TDM mingw, который включает gcc 4.4.1. Исполняемый файл связывает с двумя статическими библиотеками (.a) файлы: На них сторонняя библиотека, записанная в C; другой библиотека C++, записанная мной, который пользуется библиотекой C, предоставляет моему собственному C++ API на вершине.

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

Это не вызывает ошибки повторного определения, когда включать файлы используются многократно в другом.c или .cpp файлах в том же проекте; проблема состоит просто в том, что она генерирует одно определение на библиотеку.

Как/почему производящие функции компилятора и символы для этих подставляемых функций в обеих библиотеках? Как я могу вынудить это прекратить генерировать их в моем коде? Существует ли инструмент, который я могу выполнить для разделения дублирующихся функций из.a файла или способа заставить компоновщика проигнорировать повторные определения?

(К вашему сведению сторонняя библиотека действительно включает #ifdef __ cplusplus и экстерн "C" защита во всех его заголовках; так или иначе, если бы это было проблемой, то она не вызвала бы повторное определение символа, она вызвала бы противоположную проблему, потому что символ будет не определен или по крайней мере отличаться.)

В частности, ошибки ссылки НЕ происходят, если я связываю со сторонней библиотекой C DLL; однако затем я получаю странные отказы во время выполнения, которые, кажется, имеют отношение к моему коду, имеющему его собственную версию функций, которые он должен вызывать от DLL. (Как будто компилятор создает локальные версии функций, которые я не попросил.)

Аналогичные версии этого вопроса спросили прежде, однако, я не нашел ответ на свою ситуацию ни в одном из них:

Ответ на этот вопрос был то, что плакат был, умножают переменные определения, моей проблемой является повторное определение подставляемых функций: Повторные Ошибки Повторного определения от включения того же заголовка в нескольких cpps

Это было программой MSVC, но я использую mingw; также, проблемой плаката в этом вопросе было определение конструктора класса C++ за пределами тела класса в заголовке, в то время как моя проблема с функциями C, которые являются подставляемыми: Статическая проблема Повторного определения Lib

Этот дурак переименовал весь свой код C как файлы C++, и его код C не был C ++-safe: Повторное определение большого количества станд.:: функции при соединении

Этот просто желал знать, почему нарушение одного правила определения не было ошибкой: непредсказуемое поведение Подставляемых функций с различными определениями

12
задан Community 23 May 2017 в 12:34
поделиться

1 ответ

Сначала вы должны понять встроенную модель C99 - возможно, что-то не так с вашими заголовками. Есть два типа определений для встроенных функций с внешней (нестатической) связью

  • Внешнее определение
    Это определение функции может появляться только один раз во всей программе, в обозначенном TU. Он предоставляет экспортируемую функцию, которую можно использовать из других ЕП.

  • Встроенное определение
    Они появляются в каждой ЕП, объявленной как отдельное определение. Определения не должны быть идентичными друг другу или внешнему определению. Если они используются в библиотеке как внутренние, они могут не проверять аргументы функции, которые в противном случае выполнялись бы во внешнем определении.

Каждое определение функции имеет свои собственные локальные статические переменные , потому что их локальные объявления не имеют связи (они не являются общими, как в C ++). Определение нестатической встроенной функции будет встроенным определением, если

  • Каждое объявление функции в TU включает спецификатор inline , а
  • Ни одно объявление функции в TU не включает спецификатор extern .

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

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

// included into two TUs
void f(void); // no inline specifier
inline void f(void) { }

Следующая программа опасна, потому что компилятор может свободно использовать внешнее определение, но программа не предоставляет его

// main.c, only TU of the program
inline void g(void) {
  printf("inline definition\n");
}

int main(void) {
  g(); // could use external definition!
}

Я сделал несколько тестовых примеров с использованием GCC, которые дополнительно демонстрируют этот механизм:

main.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
  printf("inline def main.c\n");
}

// defined in TU of second inline definition
void g(void);

// defined in TU of external definition
void h(void);

int main(void) {
  // unspecified whether external definition is used!
  f();
  g();
  h();

  // will probably use external definition. But since we won't compare
  // the address taken, the compiler can still use the inline definition.
  // To prevent it, i tried and succeeded using "volatile". 
  void (*volatile fp)() = &f;
  fp();
  return 0;
}

main1.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
  printf("inline def main1.c\n");
}

void g(void) {
  f();
}

main2.c

#include <stdio.h>

// external definition!
extern inline void f(void);

inline void f(void) {
  printf("external def\n");
}


void h(void) {
  f(); // calls external def
}

Теперь программа выводит то, что мы ожидали!

$ gcc -std=c99 -O2 main.c main1.c main2.c
inline def main.c
inline def main1.c
external def
external def

Посмотрев на таблицу символов, мы увидим, что символ встроенного определения не экспортируется (из main1.o ), в то время как внешнее определение экспортируется (из main2.o ).


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

static inline void f(void) {
  printf("i'm unique in every TU\n");
}
15
ответ дан 2 December 2019 в 20:40
поделиться