2009-09-08 15 views
9

다음 상황이 있습니다 : 독립 실행 형 정적 라이브러리에서 위젯을 작성해야합니다. 그런 다음 최종 응용 프로그램 (Visual C++ 9.0, qt 4.5)과 링크됩니다. 이 정적 위젯 라이브러리는 일부 리소스 (아이콘)를 포함하고 여러 .cpp 파일로 구성되며 각 파일에는 독립 실행 형 위젯이 포함되어 있습니다. 내가 아는 한, Qt 리소스 시스템을 정적 라이브러리에서 "Q_INIT_RESOURCE (resource_file_name)"호출을 사용하여 초기화해야합니다.정적 라이브러리에 포함 된 Qt 리소스 초기화하기

가 대신 내 첫 번째 방법, 나는이 초기화 코드 정적 라이브러리 프로젝트에 별도의 init.cpp 파일을 만든
 

#include <QAbstractButton> 

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

// ... widget code .... 
 

(초기화를 포함 피하기 위해 : 나는 (정적 라이브러리의 모든 .cpp 파일에서) 다음 코드를 사용하여이 문제를 해결 모든 .cpp 파일의 코드). 그러나 이것은 작동하지 않았습니다.

왜 이것이 작동하지 않습니까?

StaticLibInitializer의이 접근법은 다양한 컴파일러와 플랫폼에서 안전하고 이식 가능합니까?

답변

10

static initialization order fiasco의 공격을 받았기 때문에 작동하지 않았습니다.

정적 개체를 초기화하는 코드는 정적 개체가 사용되는 변환 단위 (소스 파일로 읽을 수 있음)보다 크기가 커서 이동할 수 없습니다. 네가 한 것처럼. 이 정적 객체를 초기화하는 데 사용하는 스키마를 사용하려면 선언 만 init.hpp 헤더로 옮기고 정적 객체를 사용하는 각 파일에 StaticLibInitializer staticLibInitializer;을 남겨 둡니다.
위의 조언은 각 위젯이 자체 자원 만 사용한다고 가정합니다. 하나의 위젯의 자원이 다른 위젯에 의해 사용되는 상황이 있다면 정적 초기화 순서 오류로 다시 실행됩니다. 한 번만 주어진 자원을 초기화합니다 StaticLibInitializer의 확인 곱하기 인스턴스화을 한 다음 주어진 변환 단위에서 사용하고자하는 모든 리소스에 대한 StaticLibInitializer를 인스턴스화이

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

    StaticLibInitializer() 
    { 
     initialize(); 
    } 
} 

과 같은 코드를 사용하여이 상황을 관리 할 수 ​​있습니다.

+0

현재 제 상황에서는 3 개의 .cpp 파일이 있습니다. (각자 자신의 위젯을 구현합니다. 그 중 2 개는 .qrc 파일의 리소스를 사용합니다.)하지만 원래 질문으로 준 초기화 코드는 그 중 하나에서만 작동합니다 괜찮아요 (100 %, 50/50이 아님). 그래서 나는 이해할 수 없다. 왜 내가 분리 된 init에 초기화 코드를 넣었을 까.cpp 파일 내 리소스를 사용할 수 없지만이 코드가 위젯의 .cpp 파일 중 하나에 모두 제대로 작동하면 ... – cybevnm

+0

잘 작동해도 상관 없습니다 ** ** ** ** 우연히 작동합니다. 다른 컴파일러 또는 동일한 컴파일러의 다른 버전을 사용하기 시작하면 작업이 중단 될 수 있습니다. 그것은 ** 정의되지 않은 행동 **입니다. 그 이유는 위젯의 파일 중 하나에 초기화 코드가있을 때 컴파일러 **가 발생하여 리소스를 먼저 초기화하기 때문입니다. 순수한 행운, 아무것도. 화창한 하루를 0 %로 작동시키지 않으려면 * 정적 초기화 순서 실패를 피하기위한 지침을 따르십시오. –

+0

정적 초기화 순서가 컴파일 단계에서 컴파일러에 의해 정의 되었습니까? 아니면 프로그램간에 순서가 다를 수 있습니다 (재 컴파일하지 않고)? – cybevnm

6

Q_INIT_RESOURCE 매크로는 네임 스페이스에서 사용할 수 없습니다.

Qt 설명서에서 "Note :이 매크로는 네임 스페이스에서 사용할 수 없으며 main()"에서 호출해야합니다.

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

    namespace MyNamespace 
    { 
    ... 

    void myFunction() 
    { 
     initMyResource(); 
    } 
    } 

자신이 왜, 어떻게 정확히이 실패하거나 지정되지 않은 방법으로 그것을 사용하는 경우 실패하지 않습니다 봐주십시오 : 그리고 심지어 당신이 할 수없는 경우, 바로 그것을 수행하는 방법의 예를 제공합니다. 관련 코드는 QtCore에 있습니다.

+0

그러나 첫 번째 방법 (정적 라이브러리의 모든 .cpp 파일에 코드를 포함 할 때)은 익명의 네임 스페이스로도 작동합니다. – cybevnm

+0

위의''inline'을 사용하면 컴파일러가 존경받을 것이라는 보장이 없으므로 아무 것도 사지 않습니다. *이 키워드를 존중하지 않는 것은 C++ 표준에 따릅니다. 따라서이 * 솔루션 *이 가정에 기반한 경우 인라인 함수가 인라인됩니다. –

+1

'inline' 함수는 특히 ODR에서 의미가 약간 다릅니다. 우리는 모든 플랫폼에서'Q_INIT_RESOURCE'의 매크로 확장을 알지 못한다는 점을 감안할 때 필요한지 판단하기가 어렵습니다. 확실히 거기에 넣는 것이 합리적입니다. – MSalters