2010-02-07 15 views
10

Mam program C++, który kompiluję z mingw (gcc dla Windows). Używanie wersji mingw TDM zawierającej gcc 4.4.1. Plik wykonywalny łączy się z dwoma plikami biblioteki statycznej (.a): Jedna z nich to biblioteka stron trzecich napisana w języku C; druga to napisana przeze mnie biblioteka C++, która używa biblioteki C, która dostarcza moje własne API C++ na wierzchu.Wiele definicji wbudowanych funkcji podczas łączenia bibliotek statycznych

(W mojej opinii, nadmierna) część funkcji biblioteki C jest zaimplementowana w funkcjach inline. Nie można pominąć funkcji wstawianych, gdy korzystasz z biblioteki biblioteki C, ale gdy próbuję połączyć to wszystko razem, otrzymuję komunikat o błędzie z linkami mówiącą, że istnieje wiele definicji wszystkich funkcji wbudowanych - oba mam wywoływane w mojej bibliotece opakowującej C++ i takie, których nie mam, w zasadzie wszystko, co zdefiniowano w nagłówku w nagłówkach, uzyskało funkcję utworzoną dla niego zarówno w bibliotece C, jak iw bibliotece C++.

Nie powoduje błędów w wielu definicjach, gdy pliki włączeń są używane wiele razy w różnych plikach .c lub .cpp w tym samym projekcie; Problem polega tylko na tym, że generuje jedną definicję na bibliotekę.

Jak/dlaczego kompilator generuje funkcje i symbole dla tych funkcji wbudowanych w obu bibliotekach? Jak zmusić go do zaprzestania generowania ich w moim kodzie? Czy istnieje narzędzie, które można uruchomić, aby usunąć duplikaty funkcji z pliku .a, lub sposób, aby linker zignorował wiele definicji?

(FYI, trzecia biblioteka zawiera wszystkie nagłówki #ifdef __cplusplus i extern "C" - w każdym razie, gdyby to był problem, nie spowodowałaby wielokrotnej definicji symbolu, spowodowałaby przeciwny problem ponieważ symbol byłby niezdefiniowany lub co najmniej inny.)

W szczególności błędy łącza NIE występują, jeśli łącza do bibliotek DLL biblioteki C strony trzeciej; jednak otrzymuję dziwne awarie środowiska wykonawczego, które wydają się mieć związek z moim kodem posiadającym własną wersję funkcji, które powinien wywoływać z biblioteki DLL. (Tak jakby kompilator tworzył lokalne wersje funkcji, których nie prosiłem).

Podobne wersje tego pytania zostały zadane wcześniej, jednak nie znalazłem odpowiedzi na moją sytuację w żadnej z tych sytuacji:

odpowiedź na to pytanie, że plakat został wielokrotnie definiowania zmienne, mój problem jest wielokrotnością definicja funkcji inline: Repeated Multiple Definition Errors from including same header in multiple cpps

to był program MSVC, ale używam MinGW; także, problem z plakatem w tym pytaniu był definicją konstruktora klasy C++ poza ciałem klasy w nagłówku, podczas gdy mój problem dotyczy funkcji C, które są wbudowane: Static Lib Multiple Definition Problem

Ten głupiec zmienił nazwę całego swojego kodu C na C++ pliki i jego kod C nie był C++ - bezpieczne: Multiple definition of lots of std:: functions when linking

ten był po prostu chcą wiedzieć, dlaczego naruszono zasadę jedna definicja nie wystąpił błąd: unpredictable behavior of Inline functions with different definitions

+1

Zauważ, że C99 semantyka inline różnią się od jednego z C++: W C , jeśli jedno z deklaracji funkcji inline jest jawnie określone 'extern', tworzy zewnętrzną definicję - która nie jest już definicją śródliniową. Te zewnętrzne definicje nie mogą pojawić się wiele razy w programie. W C++ jawny 'extern' na takiej funkcji nie ma żadnego efektu. Najlepiej radzisz sobie z 'static inline' w C. –

+0

Zwykle definicje funkcji inline są oznaczone jako" słabe "symbole, a linker powinien sam usuwać wszystkie duplikaty. – Omnifarious

+0

Dzięki Johannesowi: przy użyciu definicji preprocesora, pliki nagłówkowe biblioteki C deklarują te funkcje "inline", jeśli znajdują się w plikach .c biblioteki C, i "extern inline", gdy są w moim projekcie. Ale nie jestem pewien, dlaczego to zrobili w ten sposób. Biblioteka C ma kod, który używa adresu funkcji jako unikalnej wartości klucza. Jako programista C++ uważam, że jako zła praktyka i naprawdę modlę się, że w ten sposób nie używali adresu funkcji inline. Czuję, że wśród tych komentarzy jest dość informacji, aby znaleźć odpowiedź, chociaż nadal nie mogę skompilować programu. – Dennis

Odpowiedz

12

Najpierw trzeba zrozumieć model inline C99 - być może coś jest nie tak z twoimi nagłówkami. Istnieją dwa rodzaje definicji funkcji inline z zewnętrznego (non-statyczne) powiązania

  • definicji Zewnętrzna
    Ta 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 JT.

  • definicja Inline
    Pojawiają się one w każdym TU gdzie zadeklarowane jako odrębnej definicji. Definicje muszą być identyczne ze sobą lub z zewnętrzną definicją. Jeśli są używane wewnętrznie w bibliotece, mogą pominąć sprawdzanie argumentów funkcji, które zostałyby wykonane w zewnętrznej definicji.

Każda definicja funkcji ma swoje lokalne zmienne statyczne, ponieważ ich lokalne deklaracje nie mają powiązania (nie są one dzielone jak w C++). Definicja non-statycznej funkcji inline będzie definicja inline jeśli

  • Każda deklaracja funkcji w TU zawiera specyfikator inline i
  • Bez deklaracji funkcji w TU zawiera specyfikator extern.

W przeciwnym razie definicja, która musi pojawić się w tej JT (ponieważ funkcje śródliniowe muszą być zdefiniowane w tej samej TU, gdzie zadeklarowano) jest definicją zewnętrzną. W wywołaniu funkcji inline nie jest określane, czy używana jest definicja zewnętrzna czy wbudowana. Ponieważ jednak funkcja zdefiniowana we wszystkich przypadkach jest wciąż taka sama (ponieważ ma połączenie zewnętrzne), jej adres jest jednakowy we wszystkich przypadkach, bez względu na to, ile pojawi się definicji śródliniowych. Jeśli więc weźmiesz adres funkcji, prawdopodobnie kompilator rozwiąże definicję zewnętrzną (szczególnie, jeśli optymalizacje są wyłączone).

Przykładem, który pokazuje złą wykorzystanie inline, ponieważ zawiera zewnętrzną definicję funkcji dwa razy w dwóch JT, powodując błąd definicja stwardnieniem

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

Poniższy program jest niebezpieczny, ponieważ kompilator jest swobodnie korzystać z definicji zewnętrznej, ale program nie przewiduje jeden

// 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 testów za pomocą GCC, które pokazują 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 wychodzi z tego, co oczekiwaliśmy!

$ 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 tablicy symboli, to widzimy, że symbol definicję inline nie jest eksportowana (z main1.o), natomiast zewnętrzna definicja jest eksportowana (z main2.o).


Teraz, jeśli twoje statyczne biblioteki mają zewnętrzną definicję swoich funkcji śródliniowych (tak jak powinny), naturalnie będą ze sobą kolidować. Rozwiązaniem jest uczynienie funkcji inline statyczną lub po prostu zmienić ich nazwy. Są zawsze zapewni definicje zewnętrznych (a więc są pełne definicje dojrzałej), ale nie są one eksportowane, ponieważ mają wewnętrzne powiązania, a tym samym nie sprzecznymi

static inline void f(void) { 
    printf("i'm unique in every TU\n"); 
} 
+0

To jest bardzo dobra odpowiedź. Niestety, wciąż nie byłem w stanie zastosować wiedzy, aby z powodzeniem skompilować mój program - próby zmiany zewnętrznych linii na statyczne inline powodują, że kończy się to błędem, że miksuje statyczne i niestatyczne definicje tej samej funkcji. Być może mogę użyć makr w moim własnym kodzie do zmiany nazw funkcji, do których włączam nagłówki, ale w nagłówkach jest ich około 100 ... – Dennis

+0

W celu uzyskania dalszych informacji, ta dyskusja wskazuje, że poziom optymalizacji kompilatora może wpływać na problem: http://lkml.indiana.edu/hypermail/linux/kernel/0408.0/1787.html – Dennis

+0

Znalazłem coś podobnego podczas budowania z MinGW gcc i odkryłem, że nagłówki MinGW wydają się być niekompatybilne z -std = Opcja gnu99. Wywołało to _lot_ wielokrotnie zdefiniowanych symboli podczas próby połączenia, ponieważ definicje funkcji zostały umieszczone w każdym pliku obiektów zbudowanym w ten sposób. –

Powiązane problemy