17

Stworzyłem system, który automatycznie rejestruje obiekty funkcji (funktory) w mapie na podstawie konstruktora instancji globalnej.Zmienna globalna C++ nie zainicjowana po połączeniu z bibliotekami statycznymi, ale OK, gdy skompilowano ją ze źródłem

W każdym pliku cpp, który definiuje funktor, istnieje globalna instancja instancji klasy rejestratora w celu zarejestrowania funktora na obiekcie singleton std::map<int, std::function<...> >.

Jest to definicja klasy rejestratora:

template 
< 
    typename map_type, 
    typename handler_type 
> 
struct registrar 
{ 
    registrar 
     (
      map_type& map_object, 
      boost::uint16_t cmd_code, 
      const handler_type& handler 
     ) 
     { 
      map_object.insert(std::pair<boost::uint16_t, handler_type>(cmd_code, handler)); 
     } 
}; 

w każdym pliku .cpp. Instancja globalna jest zdefiniowana w następujący sposób:

namespace one_way 
{ 
    static registrar <in_out_map_type, handler> 
     post_receiver(in_out_map_type::instance(), command, handlers()); 
} 

Wszystko działa dobrze, jeśli skompiluję całość cpp razem z main.cpp. Ale jeśli skompiluję plik cpp do statycznej biblioteki i połączę go z main.cpp, rejestracja nie będzie działać.

Testowałem z VC10 i GCC4.61 zarówno na Windows & i Ubuntu 11.10. Oba zawodzą.

Znalazłem a thread with the same problem, ale OP nie powiedział, czy to rozwiązał, czy nie.

Czy brakuje mi czegoś?


Edit


Dzięki za wszystkie odpowiedzi, w tym komentarze.

Każda reakcja rzeczywiście pomogła mi przemyśleć i zbadać tę metodę. Po tych wszystkich badaniach i próbach, w końcu zrezygnował z powołując się na zmiennej globalnej/statyczne do samodzielnej rejestracji ponad granicami binarnych, bo nie ma przenośny sposobem na zagwarantowanie będzie działać.

Ostatnim sposobem jest zachowanie rejestracji w jednym pliku binarnym.

+0

Nie pokazujesz, jak dzwonisz do rejestratora zarejestrowanych funkcji. Czy jest też literówka w "instancji globalnej" ('handlers()' zamiast 'handler()')? Chyba że czegoś mi brakuje, to definiuje funkcję, a nie zmienną globalną. Tak więc, wydaje mi się, że liczba brakujących punktów na obrazie ... –

+4

Poleganie na statycznej inicjalizacji w celu zarejestrowania funkcji jest prawdopodobnie podatne na błędy w dłuższej perspektywie. Czy rozważałeś właśnie dokonanie jawnej rejestracji? –

+0

Dla GCC, zobacz, czy -Wl, - pomaga całe archiwum. –

Odpowiedz

-1

Uważam, że plik obiektu z biblioteki nie jest łączony. Zobacz, jak Microsoft radzi sobie z symbolem ACrtused (przypadek wyszukiwania jest niewrażliwy, nie pamiętam przypadku i nie ma żadnego MSVC na tym komputerze).

Gdy już wiesz, jak radzą sobie z akronimem, zrób tak samo ze zmienną globalną, aby zmusić ją do połączenia.

Zostanie zaktualizowany, jeśli znajdę odpowiedź.

Oto kilka możliwości zmuszenia rzeczy do połączenia i zainicjowania w nieco wymuszonej kolejności.

Sprawdzić odpowiedź here na odpowiedź GCC.

Wygląd here dla MSVC10.

+0

Dzięki za linki. zobacz moją edycję w OP. –

0

Nie wiem, czy to będzie żadnej pomocy, czy nie, ale zadałem pytanie, które brzmi bardzo podobny:

C++ runtime knowledge of classes

Może warto przyjrzeniu?

+0

Nie mam problemu z pobraniem kodu rejestracyjnego, jeśli uwzględnię kod źródłowy w projekcie, tylko że został usunięty przez linker, jeśli jest dołączony jako biblioteka (statyczna lub dynamiczna). Umieszczenie kodu rejestracyjnego w pliku nagłówkowym może zadziałać i przejść optymalizację linkera. Ale w moim przypadku to, co jest celem korzystania z biblioteki, zmniejsza czas kompilacji, przenosząc część wolną (kod szablonu o dużej zawartości szablonowej) do biblioteki. Dziękujemy za Twój link. –

+0

Rejestracja jest faktycznie wykonywana w pliku .cc ..., o ile aplikacja używa fabryki do tworzenia zarejestrowanych rzeczy, rejestracje nie zostaną automatycznie usunięte. Sam użyłem tego samego w .a i skompilowałem i powiązałem z nim bez problemów. –

+0

W moim przypadku nie odwołuję się do fabryki w kodzie klienta, a raczej używam tylko wstępnie zarejestrowanej mapy, a więc nie eksportuję żadnego kodu związanego z rejestracją, z kolei linker nie łączy tej części, która kończy się na pusta mapa. Dodatkowym problemem jest to, że MSVC i GCC różnią się sposobem wymuszenia eksportowania symbolu. Nawet ja w końcu uszczęśliwię oba, kod będzie komplikowany. Więc się poddałem. –

7

Krótka odpowiedź dla pracy NDK na Androida. Wszystkie statyczne biblioteki, na które ten problem ma wpływ, powinny zostać dodane do zmiennej LOCAL_WHOLE_STATIC_LIBRARIES - zostaną one później odwołane przy użyciu flagi -Wl,--whole-archive i nie będą podlegały zdzieraniu.

Dłuższa odpowiedź na MSVC:

Zmienne statyczne w jednostce tłumaczenia są inicjowane przed każdym regularne kodu w jednostce tłumaczeń wykonuje. W praktyce inicjowanie następuje, gdy zostanie załadowana biblioteka wykonawcza lub dynamiczna zawierająca . Po wywołaniu funkcji \ c main() lub zakończeniu wywołania do LoadLibrary()/dlopen() zostaną zainicjowane wszystkie zmienne statyczne.

Problem opisany przez MSDN:

Konstruktorzy i przypisanie przez funkcję globalnego lub metod statycznych w deklaracji zrobić nie utworzyć odwołanie i nie przeszkodzi/opt: REF eliminacji. Efekty uboczne z takiego kodu nie powinny być zależne, gdy nie ma innych odniesień do danych.

Może to być wygodne, aby umieścić kod wynikowy z wielu jednostek tłumaczeniowych w jednym plik statyczny biblioteki umownie nazwane z \ c \ lib lub c .a przyrostka. Łącznik MSVC wykonuje analizę zależności w statycznych bibliotekach od i nie zawiera kodu, który nie jest wymieniony przez encję włączającą.

Wspólny wzór użyciu zmiennej statycznej zadeklarować i spowodować rejestrację obiekt fabryka może powiedzie się w takiej sytuacji - łącznik MSVC uzna statyczne jak jest nieosiągalny i pozbawia go od wyniku.

Solutions

Przydatny Wyszukiwarka Google: http://www.google.com/search?q=msvc+factory+static+library

Jednym rozwiązaniem jest ustawienie flagi /OPT:NOREF łącznika na tym podmiocie. Jednak jest to ustawienie typu "wszystko albo nic" i będzie wymagało, aby wszystkie dołączone biblioteki były w pełni łączone.

Jeśli coś w pliku zawierającym dane statyczne jest przywoływane (bezpośrednio lub pośrednio) przez encję włączającą, to zgodnie z regułami języka statyczny sam musi zostać zachowany.

Najbardziej podstawowym podejściem jest umieszczenie fałszywej funkcji w pliku i odniesienie do niej z w miejscu, o którym wiadomo, że jest osiągalne.

Innym rozwiązaniem jest użycie znacznika linkera /INCLUDE w celu odniesienia do encji w pliku problemu. Zakładając podmiot o nazwie DummyForLinkProblem, można to zrobić w źródle w TYM jednostki:

#pragma comment(linker, "/include:DummyForLinkProblem") 

ZooLib's Solution

podmioty ZooLib obecnie dotknięte tym problemem są te, w ZFile_Win.cpp, ZGRgnRep_HRGN. cpp, ZNet_Internet_WinSock.cpp, ZStreamRWCon_SSL_Win.cpp, ZTextCoder_Win.cpp i ZUnicode_Normalize_Win.cpp.

Mamy #include ZCompat_MSVCStaticLib.h w odpowiednich plikach nagłówkowych i umieszczamy w każdym z nich ZMACRO_MSVCStaticLib_Reference(ModifiedFileName). W plikach cpp umieszczamy ZMACRO_MSVCStaticLib_cpp(ModifiedFileName). Nazwa ModifiedFileName jest ogólnie nazwą pliku , z usuniętym wiodącym rozszerzeniem Z i plikiem, takim samym stylem, jak , użytym w makrach ZCONFIG_API_XXX.

Aby upewnić się, że plik wykonywalny lub biblioteka nie usuwa tych elementów, wystarczy #include odpowiedni plik nagłówkowy ze znanego kodu referencyjnego w encji włączającej. To spowoduje, że wystąpi niewykonalne odniesienie i wszystko będzie działać zgodnie z oczekiwaniami.