【问题标题】:Initializing qt resources embedded in static library初始化嵌入在静态库中的 qt 资源
【发布时间】:2010-11-26 10:40:09
【问题描述】:

我有下一个情况:我需要在独立静态库中创建小部件,然后将其与最终应用程序(visual c++ 9.0,qt 4.5)链接。 这个静态小部件库包含一些资源(图标),并由几个 .cpp 文件组成(每个文件都包含独立的小部件)。据我所知,如果我在静态库中使用它们(资源),我必须初始化 qt 资源系统,并调用“Q_INIT_RESOURCE(resource_file_name)”。我用下一个代码解决了这个问题(在静态库中的每个 .cpp 文件中):

#include <QAbstractButton> namespace { struct StaticLibInitializer { StaticLibInitializer() { Q_INIT_RESOURCE(qtwidgets_custom_resources); } }; StaticLibInitializer staticLibInitializer; } // ... widget code ....

我没有使用第一种方法,而是在静态库项目中使用初始化代码创建了单独的 init.cpp 文件(以避免在每个 .cpp 文件中包含初始化代码),但这不起作用。

为什么这不起作用?

这种使用 StaticLibInitializer 的方法在各种编译器和平台之间是否安全且可移植?

【问题讨论】:

    标签: c++ qt static-libraries static-order-fiasco


    【解决方案1】:

    它不起作用,因为你设法被static initialization order fiasco 击中。

    您不能移动初始化静态对象的代码,使其超出使用这些静态对象的翻译单元(您可以将其作为源文件读取)。不是你做的那样。如果您想使用您用来初始化这些静态对象的方案,而不是仅将声明移动到您的 init.hpp 标头,但在每个使用静态对象的文件中保留 instatiations StaticLibInitializer staticLibInitializer;
    以上建议假定每个小部件仅使用自己的资源。如果您遇到一个小部件的资源被另一个小部件使用的情况,您会再次遇到静态初始化命令失败。您可以使用这样的代码来管理这种情况

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

    确保 StaticLibInitializer 的多个实例化只会初始化给定资源一次,然后为您将在给定翻译单元中使用的每个资源实例化 StaticLibInitializer。

    【讨论】:

    • 在我目前的情况下,我有三个 .cpp 文件(每个都实现了自己的小部件,其中两个使用 .qrc 文件中的资源),但是我在原始问题中给出的初始化代码,只有在其中一个中,一切正常(100%,而不是 50/50)。所以我不明白,为什么当我将初始化代码放在单独的 init.cpp 文件中时,我不能使用我的资源,但是当这个小部件的 .cpp 文件之一中的代码一切正常时......
    • 没关系它可以正常工作现在 :) 它只是偶然的。当您开始使用另一个编译器甚至是同一编译器的另一个版本时,它可能会停止工作。这是未定义的行为。它现在工作的原因是因为当您在小部件的文件之一中有初始化代码时,编译器发生首先初始化您的资源。纯属运气,仅此而已。如果您不想让您的程序在某个晴天以 0% 的速度运行,请按照说明避免静态初始化命令失败
    • 静态初始化顺序是由编译器在编译阶段定义的,还是程序重启时顺序可能不同(无需重新编译)?
    • 不,它在程序启动之间没有变化,它通常取决于链接顺序。一些编译器允许通过扩展指定跨翻译单元的初始化顺序。
    • 非常感谢您的解释!
    【解决方案2】:

    Q_INIT_RESOURCE 宏不能在命名空间中使用。

    让我引用 qt 手册:“注意:这个宏不能在命名空间中使用。它应该从 main() 调用”。甚至它还为您提供了一个示例,如果这是不可能的,如何正确地做到这一点:

      inline void initMyResource() { Q_INIT_RESOURCE(myapp); }
    
        namespace MyNamespace
        {
         ...
    
         void myFunction()
         {
             initMyResource();
         }
      }
    

    如果您以未指定的方式使用它,请自行检查它失败或不失败的原因和方式。相关代码在QtCore中。

    【讨论】:

    • 但是在第一种方法中(当我在静态库的每个 .cpp 文件中包含代码时)这有效(即使使用匿名命名空间)。
    • 使用上面的inline 不会给您带来任何好处,因为您无法保证编译器会尊重它。 尊重这个关键字是符合c++标准的。因此,如果这个 解决方案 是基于假设内联函数将被内联,那么它就会被破坏。
    • inline 函数的语义略有不同,尤其是在涉及 ODR 时。考虑到我们不知道Q_INIT_RESOURCE在所有平台上的宏扩展,很难判断是否需要。放在那里当然是合理的。
    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 2018-07-05
    相关资源
    最近更新 更多