2009-09-08 17 views
9

Mam następną sytuację: Potrzebuję utworzyć widget w samodzielnej bibliotece statycznej, która następnie zostanie połączona z aplikacją końcową (wizualne C++ 9.0, qt 4.5). Ta statyczna biblioteka widgetów zawiera niektóre zasoby (ikony) i składa się z kilku plików .cpp (każdy zawiera samodzielny widget). O ile mi wiadomo, muszę zainicjować system zasobów qt, jeśli użyję ich (zasobów) w bibliotece statycznej, z wywołaniem do "Q_INIT_RESOURCE (resource_file_name)". Rozwiązałem to z kolejnego kodu (w każdym pliku .cpp w bibliotece statyczne):Inicjowanie zasobów qt osadzonych w bibliotece statycznej

 

#include <QAbstractButton> 

namespace { 
struct StaticLibInitializer 
{ 
    StaticLibInitializer() 
    { 
     Q_INIT_RESOURCE(qtwidgets_custom_resources); 
    } 
}; 
StaticLibInitializer staticLibInitializer; 
} 

// ... widget code .... 
 

Zamiast moim pierwszym podejściu, stworzyłem osobny plik init.cpp w projekcie biblioteki statycznej z kodem inicjującym (aby uniknąć tym inicjalizacji kod w każdym pliku .cpp), ale to nie zadziałało.

Dlaczego to nie zadziałało?

Czy to podejście ze StaticLibInitializer jest bezpieczne i przenośne wśród różnych kompilatorów i platform?

Odpowiedz

10

To nie zadziałało, ponieważ udało Ci się zostać trafionym przez static initialization order fiasco.

Nie można przenieść kodu, który inicjalizuje statyczne obiekty poza zespołem tłumaczeniowym (można go odczytać jako plik źródłowy), w którym są używane te statyczne obiekty. Nie tak, jak to zrobiłeś. Jeśli chcesz użyć schematu, którego używasz do inicjowania tych obiektów statycznych, zamiast przenosić tylko deklaracje do nagłówka init.hpp, ale pozostaw instatiations StaticLibInitializer staticLibInitializer; w każdym pliku, który używa obiektów statycznych.
Powyższe porady zakładają, że każdy widget wykorzystuje tylko własne zasoby. Jeśli masz sytuację, w której zasoby jednego widgetu są używane przez inny widget, ponownie uruchomisz statyczne kolejkowanie inicjalizacji zamówienia. Można zarządzać tej sytuacji przy użyciu kodu jak ten

StaticLibInitializer 
{ 
    void initialize() 
    { 
     static Q_INIT_RESOURCE(qtwidgets_custom_resources); 
    } 

    StaticLibInitializer() 
    { 
     initialize(); 
    } 
} 

aby upewnić dawałaby wielokrotnością StaticLibInitializer zainicjuje danego zasobu tylko raz, a następnie wystąpienia StaticLibInitializer dla każdego zasobu, który będzie używany w danej jednostce tłumaczeniowej.

+0

W mojej obecnej sytuacji mam trzy pliki .cpp (każdy z nich implementuje swój własny widget, dwa z nich używają zasobów z pliku .qrc), ale kod inicjujący, który podałem w oryginalnym pytaniu, tylko w jednym z nich i wszystkich pracach dobrze (100%, nie 50/50). Więc nie mogę zrozumieć, dlaczego kiedy wstawiłem kod inicjujący w osobnym init.Plik cpp Nie mogę używać moich zasobów, ale kiedy ten kod w jednym z plików .cpp widżetu działa dobrze ... – cybevnm

+0

Nie ma znaczenia, że ​​działa dobrze ** teraz ** :) Działa tylko przez przypadek. Może przestać działać w momencie rozpoczęcia korzystania z innego kompilatora lub nawet innej wersji tego samego kompilatora. To ** NIEPRZESTRZEGANE ZACHOWANIE **. Powodem, dla którego teraz działa, jest to, że gdy masz kod inicjalizacyjny w jednym z plików widżetu, kompilator ** dzieje się **, aby najpierw zainicjować twoje zasoby. Szczęście, nic więcej. Jeśli nie chcesz, aby twój program działał 0% jeden słoneczny dzień, postępuj zgodnie z instrukcjami, aby uniknąć * statycznego niepowodzenia inicjalizacji *. –

+0

Czy porządek inicjalizacji statycznej jest zdefiniowany przez kompilator w fazie kompilacji, czy kolejność może zmieniać się między programami uruchamiającymi się ponownie (bez ponownej kompilacji)? – cybevnm

6

Makro Q_INIT_RESOURCE nie może być używane w przestrzeni nazw.

Pozwólcie, że zacytuję z podręcznika qt: "Uwaga: Tego makra nie można używać w przestrzeni nazw.Należy go wywołać z głównej()". I nawet to daje przykład jak zrobić to dobrze, jeśli nie jest to możliwe:

inline void initMyResource() { Q_INIT_RESOURCE(myapp); } 

    namespace MyNamespace 
    { 
    ... 

    void myFunction() 
    { 
     initMyResource(); 
    } 
    } 

Proszę spojrzeć sobie pytanie, dlaczego i jak dokładnie to się nie powiedzie, lub nie powiedzie się, jeśli używasz go w nieokreślonym sposób. Odpowiedni kod znajduje się w QtCore.

+0

Ale w pierwszym podejściu (gdy dołączam kod do każdego pliku .cpp biblioteki statycznej) działa to (nawet z anonimowym obszarem nazw). – cybevnm

+0

Używanie 'inline' powyżej niczego nie kupuje, ponieważ nie masz żadnej gwarancji, że będzie przestrzegany przez kompilator. * Nie * respektowanie tego słowa kluczowego jest zgodne ze standardem C++. Więc jeśli to * rozwiązanie * jest oparte na założeniu, funkcja inline będzie wstawiona, a to zepsute. –

+1

Funkcje 'inline' mają nieco inną semantykę, szczególnie jeśli chodzi o ODR. Biorąc pod uwagę, że nie znamy makro-rozszerzenia 'Q_INIT_RESOURCE' na wszystkich platformach, trudno jest ocenić, czy jest to potrzebne. Z pewnością jest to uzasadnione. – MSalters

Powiązane problemy