2009-09-08 5 views
9

Ich habe nächste Situation: Ich muss Widget in Standalone statische Bibliothek erstellen, die dann mit der endgültigen Anwendung (Visual C++ 9.0, Qt 4.5) verknüpft wird. Diese statische Widgetbibliothek enthält einige Ressourcen (Symbole) und besteht aus mehreren CPP-Dateien (die jeweils ein eigenständiges Widget enthalten). So weit ich weiß, muss ich qt resource system initialisieren, wenn ich sie (Ressourcen) in der statischen Bibliothek benutze, mit dem Aufruf von "Q_INIT_RESOURCE (resource_file_name)". Ich löste dies mit dem nächsten Code (in jeder CPP-Datei in statischer Bibliothek):Initialisierung von qt-Ressourcen in der statischen Bibliothek eingebettet

 

#include <QAbstractButton> 

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

// ... widget code .... 
 

Statt meinem ersten Ansatz, ich getrennte init.cpp Datei in statischem Bibliothek-Projekt mit Initialisierungscode erstellt habe (zur Vermeidung von Initialisierung einschließlich Code in jeder CPP-Datei), aber das hat nicht funktioniert.

Warum das nicht funktioniert?

Ist dieser Ansatz mit StaticLibInitializer sicher und portabel zwischen verschiedenen Compilern und Plattformen?

Antwort

10

Es hat nicht funktioniert, weil Sie es geschafft haben, von static initialization order fiasco getroffen zu werden.

Sie können Ihren Code, der statische Objekte initialisiert, nicht über die Größe der Übersetzungseinheit verschieben (Sie können sie als Quelldatei lesen), wenn diese statischen Objekte verwendet werden. Nicht so, wie du es gemacht hast. Wenn Sie das Schema verwenden möchten, das Sie verwenden, um diese statischen Objekte zu initialisieren, dann verschieben Sie nur Deklarationen in Ihren init.hpp-Header, aber lassen Sie in jeder Datei, die statische Objekte verwendet, die Einträge StaticLibInitializer staticLibInitializer;.
Der obige Rat geht davon aus, dass jedes Widget nur seine eigenen Ressourcen verwendet. Wenn Sie eine Situation haben, in der die Ressourcen eines Widgets von einem anderen Widget verwendet werden, führen Sie das statische Initialisierungsreihenfolge-Fiasko erneut aus. Sie können mit Code wie diese

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

    StaticLibInitializer() 
    { 
     initialize(); 
    } 
} 

diese Situation verwalten, dass mehrfach Instanzierungen StaticLibInitializer machen wird nur einmal gegebene Ressource initialisieren und dann StaticLibInitializer instanziiert für jede Ressource, die Sie in bestimmten Übersetzungseinheit verwenden werden.

+0

In meiner aktuellen Situation habe ich drei .cpp-Dateien (jeder von ihnen implementiert sein eigenes Widget, zwei von ihnen verwenden Ressourcen aus .qrc-Datei), aber Initialisierungscode, den ich in der ursprünglichen Frage gab, nur in einer von ihnen und alle funktioniert gut (100%, nicht 50/50). Also ich kann nicht verstehen, warum, wenn ich Initialisierungscode in separaten init einlege.CPP-Datei Ich kann meine Ressourcen nicht verwenden, aber wenn dieser Code in einer der .cpp-Datei des Widgets funktioniert, funktioniert alles einwandfrei ... – cybevnm

+0

Es macht nichts, es funktioniert gut ** jetzt ** :) Es funktioniert nur zufällig. Es kann nicht mehr funktionieren, sobald Sie einen anderen Compiler oder sogar eine andere Version desselben Compilers verwenden. Es ist ** UNDEFINIERTES VERHALTEN **. Der Grund, warum es jetzt funktioniert, liegt darin, dass, wenn Sie Initialisierungscode in einer der Dateien des Widgets haben, der Compiler ** passiert **, um Ihre Ressourcen zuerst zu initialisieren. Pures Glück, mehr nicht. Wenn Sie nicht möchten, dass Ihr Programm 0% an einem sonnigen Tag funktioniert, befolgen Sie die Anweisungen, um * statische Initialisierungsreihenfolge Fiasko * zu vermeiden. –

+0

Wird die statische Initialisierungsreihenfolge vom Compiler bei der Kompilierungsphase definiert, oder kann die Reihenfolge zwischen den Programmen neu gestartet werden (ohne Neukompilierung)? – cybevnm

6

Der Makro Q_INIT_RESOURCE kann nicht in einem Namespace verwendet werden.

Lassen Sie mich aus dem QT-Handbuch zitieren: "Hinweis: Dieses Makro kann nicht in einem Namespace verwendet werden. Es sollte von main()" aufgerufen werden. Und auch es gibt Ihnen ein Beispiel, wie man es richtig machen, wenn dies nicht möglich ist:

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

    namespace MyNamespace 
    { 
    ... 

    void myFunction() 
    { 
     initMyResource(); 
    } 
    } 

Bitte schauen Sie sich, warum und wie genau es ausfällt oder nicht scheitern, wenn Sie es in einer nicht spezifizierten Art und Weise nutzen. Der entsprechende Code ist in QtCore.

+0

Aber in erster Annäherung (wenn ich Code in jede CPP-Datei der statischen Bibliothek einfüge), funktioniert das (sogar mit anonymem Namespace). – cybevnm

+0

Mit 'inline' oben kaufen Sie nichts, da Sie keine Garantie haben, dass es von einem Compiler respektiert wird. * Dieses Schlüsselwort entspricht nicht dem C++ - Standard. Also, wenn diese * Lösung * auf Annahme basiert, wird die Inline-Funktion inline sein, es ist gebrochen. –

+1

Inline-Funktionen haben eine etwas andere Semantik, besonders wenn es um die ODR geht. Wenn wir die Makro-Erweiterung von 'Q_INIT_RESOURCE' nicht auf allen Plattformen kennen, ist es schwer zu beurteilen, ob sie benötigt wird. Es ist sicherlich vernünftig, es dort zu sagen. – MSalters