Wielokrotne definiowanie funkcji inline podczas łączenia bibliotek statycznych

Mam program C++, który kompiluję z mingw (gcc Dla Windows). Korzystanie z wersji TDM mingw, która zawiera gcc 4.4.1. Wykonywalne linki do dwóch statycznych bibliotek (.a) pliki: na jednym z nich jest biblioteka innej firmy napisana w C; druga to biblioteka C++, napisana przeze mnie, która korzysta z biblioteki C zapewnia moje własne API C++ na górze.

(moim zdaniem zbyt duża) część funkcjonalności biblioteki C jest zaimplementowana w funkcjach wbudowanych. Nie można uniknąć włączenia inline funkcje podczas korzystania z API biblioteki C, ale kiedy próbuję połączyć to wszystko razem, dostaję błędy łącza mówiąc, że istnieje wiele definicji wszystkich funkcji inline - zarówno te, które wywołałem w mojej bibliotece C++ wrapper i te, których nie mam, w zasadzie nic zdefiniowanego inline w nagłówkach dostał funkcję utworzoną dla niego zarówno w bibliotece C i bibliotece C++.

Nie powoduje wielu błędów definicji, gdy pliki nagłówkowe są używane wielokrotnie w inaczej .c lub .pliki cpp w tym samym projekcie; problem polega na tym, że generuje jedną definicję na bibliotekę.

Jak / dlaczego kompilator generuje funkcje i symbole dla tych funkcji inline w obu bibliotekach? Jak mogę zmusić go do zaprzestania generowania ich w moim kodzie? Czy istnieje narzędzie, które mogę uruchomić, aby usunąć zduplikowane funkcje z.plik, czy sposób, aby linker ignorował wiele definicji?

(FYI, biblioteka stron trzecich zawiera #ifdef _ _ cplusplus i extern" C " strzeże we wszystkich nagłówkach; w każdym razie, gdyby to był problem, nie spowodowałby wielokrotnej definicji symbolu, spowodowałby przeciwny problem, ponieważ symbol byłby niezdefiniowany lub przynajmniej inny.)

W szczególności, błędy łącza nie występują, jeśli łącze do biblioteki DLL strony trzeciej c; jednak wtedy dostaję dziwne błędy uruchomieniowe, które wydają się mieć do czynienia z moim kodem posiadającym własną wersję funkcji, które powinny być wywołane Z DLL. (Jakby kompilator był tworzenie lokalnych wersji funkcji, o które nie prosiłem.)

Podobne wersje tego pytania były już zadawane, jednak nie znalazłem odpowiedzi na moją sytuację w żadnej z nich:

Odpowiedź na to pytanie była taka, że plakat był wielokrotnym definiowaniem zmiennych , moim problemem jest wielokrotne definiowanie funkcji inline: powtarzające się wielokrotne błędy definicji z włączania tego samego nagłówka w wielu cpps

To był program MSVC, ale używam mingw; również, problem plakatu w tym pytaniu była definicja konstruktora klasy C++ poza ciałem klasy w nagłówku, podczas gdy mój problem jest z funkcjami C, które są inline: Static lib Multiple Definition Problem

Ten głupiec zmienił nazwę całego kodu C na pliki C++, a jego kod C nie był bezpieczny w C++: wielokrotna definicja wielu funkcji std:: podczas łączenia

Ten chciał tylko wiedzieć, dlaczego złamanie zasady jednej definicji nie było błąd: nieprzewidywalne zachowanie funkcji Inline o różnych definicjach

Author: Community, 2010-02-07

1 answers

Najpierw musisz zrozumieć model C99 inline-być może coś jest nie tak z Twoimi nagłówkami. Istnieją dwa rodzaje definicji funkcji inline z zewnętrznym (niestatycznym) połączeniem

  • Definicja Zewnętrzna
    Taka definicja funkcji może pojawić się tylko raz w całym programie, w wyznaczonym TU. Zapewnia wyeksportowaną funkcję, która może być używana z innych tu.

  • Definicja Inline
    Pojawiają się one w każdym TU, gdzie deklarowane jako odrębna definicja. Definicje Nie muszą być identyczne ze sobą lub z definicją zewnętrzną. Jeśli są używane wewnętrznie w bibliotece, mogą pomijać sprawdzanie argumentów funkcji, które w przeciwnym razie byłyby wykonywane w definicji zewnętrznej.

każda definicja funkcji ma swoje własne lokalne zmienne statyczne , ponieważ ich lokalne deklaracje nie mają powiązania (nie są współdzielone jak w C++). Definicja a niestatyczna funkcja inline będzie definicją inline, jeśli

  • każda deklaracja funkcji w TU zawiera specyfikator inline i
  • Żadna deklaracja funkcji w TU nie zawiera specyfikacji extern.

W Przeciwnym Razie definicja, która musi pojawić się w tym TU (ponieważ funkcje inline muszą być zdefiniowane w tym samym TU, gdzie zadeklarowane), jest definicją zewnętrzną. W wywołaniu funkcji inline nie jest sprecyzowane, czy zewnętrzna czy wewnętrzna definicja jest używany . Jednakże, ponieważ funkcja zdefiniowana we wszystkich przypadkach jest nadal taka sama (ponieważ ma zewnętrzne powiązanie), jej adres jest równy we wszystkich przypadkach, bez względu na to, ile definicji inline się pojawia. Więc jeśli weźmiesz adres funkcji, prawdopodobnie kompilator zmieni definicję na zewnętrzną(zwłaszcza jeśli optymalizacje są wyłączone).

Przykład, który demonstruje niewłaściwe użycie inline, ponieważ zawiera zewnętrzną definicję funkcji dwa razy w dwóch TUs, wywołanie błędu wielokrotnej definicji

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

Poniższy program jest niebezpieczny, ponieważ kompilator może swobodnie używać definicji zewnętrznej, ale program nie udostępnia jej

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

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

Zrobiłem kilka przypadków testowych przy użyciu GCC, które demonstrują mechanizm dalej:

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
}

Teraz program wyświetla to, co my oczekiwane!

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

Patrząc na tabelę symboli, zobaczymy, że symbol definicji wbudowanej nie jest eksportowany( z main1.o), podczas gdy definicja zewnętrzna jest eksportowana (z main2.o).


Teraz, jeśli Twoje biblioteki statyczne mają zewnętrzną definicję swoich funkcji wbudowanych (Tak jak powinny), będą ze sobą w naturalny sposób sprzeczne. Rozwiązanie polega na tym, aby funkcje wbudowane stały lub po prostu zmienić ich nazwę. Będą one zawsze zawierać definicje zewnętrzne (tak są one pełnowartościowymi definicjami), ale nie są eksportowane, ponieważ mają wewnętrzne powiązania, a więc nie są sprzeczne

static inline void f(void) {
  printf("i'm unique in every TU\n");
}
 14
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-07 23:23:55