13

I have next situation: I need to create widget in standalone static library, which then will be linked with final application (visual c++ 9.0, qt 4.5). This static widget library contains some resources (icons), and consist of a several .cpp files (each contains standalone widget). As far as I know, i must initialize qt resource system, if i use them (resources) in static library, with call to "Q_INIT_RESOURCE( resource_file_name )". I solved this with next code (in every .cpp file in static library):


#include <QAbstractButton>

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

// ... widget code ....

Instead of my first approach, I have created separate init.cpp file in static library project with initialization code (to avoid including initialization code in every .cpp file), but this didn't work.

Why this didn't work ?

Is this approach with StaticLibInitializer is safe and portable among various compilers and platforms ?

Piotr Dobrogost
  • 38,049
  • 34
  • 218
  • 341
cybevnm
  • 2,506
  • 4
  • 28
  • 33

2 Answers2

11

It didn't work because you managed to get hit by static initialization order fiasco.

You can't move your code that initializes static objects outsize the translation unit (you can read it as source file) where these static objects are used. Not the way you did it. If you want to use the scheme you are using to initialize these static objects than move only declarations to your init.hpp header but leave instatiations StaticLibInitializer staticLibInitializer; in each file which uses static objects.
Above advice assumes each widget uses only its own resources. If you have situation in which one widget's resources are used by another widget you run into static initialization order fiasco again. You can manage this situation by using code like this

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

    StaticLibInitializer()
    {
         initialize();
    }
}

to make sure multiply instantiations of StaticLibInitializer will initialize given resource only once and then instantiate StaticLibInitializer for every resource you are going to use in given translation unit.

Alexander Shishenko
  • 890
  • 1
  • 9
  • 26
Piotr Dobrogost
  • 38,049
  • 34
  • 218
  • 341
  • In my current situation I have three .cpp files (each of them implements its own widget, two of them use resources from .qrc file), but initialization code, which I gave in original question, only in one of them and all works fine (100%, not 50/50). So I can't understand, why when I put initialization code in separate init.cpp file I can't use my resources, but when this code in one of the widget's .cpp file all works fine... – cybevnm Sep 15 '09 at 08:36
  • It doesn't matter it works fine **now** :) It works only by accident. It can stop working the moment you start using another compiler or even another version of the same compiler. It's **UNDEFINED BEHAVIOR**. The reason it works now is because when you have initialization code in one of the widget's files compiler **happens** to initialize your resources first. Pure luck, nothing more. If you don't want to get your program working 0% one sunny day follow instructions to avoid *static initialization order fiasco*. – Piotr Dobrogost Sep 15 '09 at 12:45
  • Is static initialization order defined by compiler at compilation phase, Or order may varying between programs restarts (without recompilation) ? – cybevnm Sep 15 '09 at 13:19
  • No, it does not vary between program starts, it usually depends on the linking order. Some compilers allow to specifiy the initialization order accross translation units with extensions. – Gunther Piez Sep 15 '09 at 23:18
  • Thanks a lot for you explanations! – cybevnm Sep 16 '09 at 10:01
7

The Q_INIT_RESOURCE macro can't be used in a namespace.

Let me quote from the qt manual: "Note: This macro cannot be used in a namespace. It should be called from main()". And even it gives you an example how to do it right, if this is not possible:

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

    namespace MyNamespace
    {
     ...

     void myFunction()
     {
         initMyResource();
     }
  }

Please look yourself why and how exactly it fails or does not fail if you use it in an unspecified way. The relevant code is in QtCore.

Gunther Piez
  • 28,058
  • 6
  • 62
  • 101
  • But in first approach (when I include code in every .cpp file of static library) this works (even with anonymous namespace). – cybevnm Sep 08 '09 at 09:29
  • Using `inline` above doesn't buy you anything as you have no guarantee it will be respected by a compiler. *Not* respecting this keyword is in accordance with c++ standard. So if this *solution* is based on assumption inline function will be inlined it's broken. – Piotr Dobrogost Sep 14 '09 at 07:55
  • 1
    `inline` functions have slightly different semantics, especially when it comes to the ODR. Considering we don't know the macro expansion of `Q_INIT_RESOURCE` on all platforms, it's hard to judge if it's needed. It's certainly reasonable to put it there. – MSalters Sep 14 '09 at 08:39