views:

931

answers:

2

Hi folks,

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 ?

+1  A: 

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.

drhirsch
But in first approach (when I include code in every .cpp file of static library) this works (even with anonymous namespace).
vnm
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
`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
+2  A: 

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.

Piotr Dobrogost
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...
vnm
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
Is static initialization order defined by compiler at compilation phase, Or order may varying between programs restarts (without recompilation) ?
vnm
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.
drhirsch
Thanks a lot for you explanations!
vnm