Linux에서 동적 라이브러리 및 정적 라이브러리를 만들고 사용하기위한 QT-Guide

앞에 쓰기

주위

  • Ubuntu 18.04
  • Qt 5.9.3

Qt 라이브러리 분류

QtCreator 에서 새 프로젝트 만들고 Library - C ++ 라이브러리를 선택 합니다.

여기에 사진 설명 삽입
세 가지 유형, 즉 다음을 제공한다는 것을 알 수 있습니다.

  • 공유 라이브러리 (DLL)
    타입의 선택은, 동적 링크 라이브러리를 생성한다 linux(가)이다 *.so에서 Windows*.dll.

  • 정적 링크 라이브러리 정적 링크 라이브러리
    를 생성하려면이 유형을 선택하십시오 *.a. 최종 생성 된 라이브러리는 입니다.


  • 이 유형의 Qt 플러그인은 플러그인 과 관련이 PASS있습니다.

ps : 프로그램이 컴파일 될 때 정적 라이브러리가 대상 코드에 링크되고 프로그램이 실행 중일 때 정적 라이브러리가 더 이상 필요하지 않습니다. 컴파일 후 프로그램 파일은 크지 만 로딩이 빠르고 격리가 좋습니다. 동적 라이브러리는 프로그램이 컴파일 될 때 대상 코드에 링크되지 않지만 프로그램이 실행될 때로드되므로 동적 라이브러리가 실행될 때 프로그램에 있어야합니다. 여러 응용 프로그램을 한 번만 메모리에 동적 라이브러리를로드, 동일한 동적 라이브러리, 시간 발사 여러 응용 프로그램을 사용할 수 있습니다 1 .

라이브러리 파일을 생성하는 방법

공유 라이브러리

새 프로젝트, 템플릿 선택 Liabary- C++库선택 유형 共享库.

Location그리고 Kits다음과 같이 약간 모듈을 선택했습니다.

여기에 사진 설명 삽입
기본적으로 선택되어 QtCore있으며, 모듈을 제거하면 Qt많은 유형을 사용할 수 없으며 C/C++의 데이터 유형 만 사용할 수 있습니다 .
라이브러리에 포함 된 GUI경우 또한 확인해야합니다 QtGui.

생성되면 다음 문서 : 일부 매크로 정의가 저장된 파일
여기에 사진 설명 삽입
이 있습니다 .testso_global.h

#include <QtCore/qglobal.h>

#if defined(TESTSO_LIBRARY)
#  define TESTSOSHARED_EXPORT Q_DECL_EXPORT
#else
#  define TESTSOSHARED_EXPORT Q_DECL_IMPORT
#endif

라이브러리가 클래스 만 빌드하는 so库경우 문제가 발생할 때 백 사용을 피하기 위해 위의 매크로 정의 헤더 파일의 일부가되어에 추가 testso.h한 다음 주석 처리 할 수 #include "testso_global.h"있습니다.

그런 다음 testso_global.h삭제할 수 있습니다 . 이런 식으로 암시 적 링크가 사용되는 경우에만 이식이 필요합니다 testso.h.

라이브러리에 여러 클래스가있는 경우 보관하는 것이 더 편리합니다. testso_global.hShaoshang 이 필요할 때 암시 적 링크 입니다.

여기 testso에 두 가지 기능 테스트를 추가했습니다.

testo.h

#ifndef TESTSO_H
#define TESTSO_H

#include "testso_global.h"
#include <QString>
#include <QDebug>

class TESTSOSHARED_EXPORT Testso
{
    
    

public:
    Testso();

    QString getName();
    void testDebug();
};

#endif // TESTSO_H

testo.c

#include "testso.h"

Testso::Testso()
{
    
    
}

QString Testso::getName(){
    
    
    QString re = "testso";
    return re;
}

void Testso::testDebug(){
    
    
    qDebug() << "Debug test success.";
}

빌드 만하면 공유 라이브러리가 직접 생성되며 실행하면 다음과 같습니다.

여기에 사진 설명 삽입
컴파일이 성공했음을 알리는 팝업 창이 나타나고 깔끔하게 정리 된 4 명의 형제가 생겼습니다.

여기에 사진 설명 삽입

정적 라이브러리

정적 라이브러리의 생성은 작동중인 동적 라이브러리와 동일하므로 반복하지 않습니다.

결과 파일은 다음과 같습니다.
여기에 사진 설명 삽입
동적 라이브러리와 비교하여 하나 더 적은 *_global.h파일 을 찾을 수 있습니다 .

테스트를위한 함수도 추가합니다.

test_staticdll.h

#ifndef TEST_STATICDLL_H
#define TEST_STATICDLL_H

#include <QDebug>


class Test_staticdll
{
    
    

public:
    Test_staticdll();
    void test();
};

#endif // TEST_STATICDLL_H

test_staticdll.c

#include "test_staticdll.h"


Test_staticdll::Test_staticdll()
{
    
    
}

void Test_staticdll::test(){
    
    
    qDebug() << "test Static dll is success.";
}

컴파일 후 *.a파일 이 생성되었음을 발견했습니다.

여기에 사진 설명 삽입

라이브러리 파일을 호출하는 방법

암시 적 링크

Qt 도구의 도움으로

Qt 도구의 도움으로 添加库-> 外部库-> 选择平台->选择库文件

여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 사진 설명 삽입

가져온 후 *.pro자동은 다음을 추가합니다.

DISTFILES +=

unix:!macx: LIBS += -L$$PWD/../build-testso-Desktop_Qt_5_9_3_GCC_64bit-Debug/ -ltestso

INCLUDEPATH += $$PWD/../build-testso-Desktop_Qt_5_9_3_GCC_64bit-Debug
DEPENDPATH += $$PWD/../build-testso-Desktop_Qt_5_9_3_GCC_64bit-Debug

그런 다음 testso.h프로젝트 경로에 복사됩니다. testso_global.h통화가있는 경우 하나를 설정하고 복사해야합니다.

여기에 사진 설명 삽입
그런 다음 프로젝트에서 사용해야하는 헤더 파일을 가져옵니다.

데모 사용

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    Testso *test = new Testso();

    ui->label->setText(test->getName());
    test->testDebug();
}
MainWindow::~MainWindow()
{
    
    
    delete ui;
}

효과
여기에 사진 설명 삽입

manully 추가

즉, 도움없이 가져 오기 라이브러리 Qt向导*.pro실현을 직접 수정합니다 .
생성 된 이전 마법사를 참조 할 수 있습니다.*.pro

보충 지식 :

  • -L 다음이 폴더임을 나타냅니다. 프로젝트는이 경로를 라이브러리 파일의 검색 경로에 추가합니다.
  • -l 다음은 라이브러리 파일의 이름임을 나타냅니다.
  • $$PWD 현재 경로를 나타냅니다.
  • /.. 이전 경로로 돌아가도록 나타냅니다.

명시 적 링크

참조 전용 데모

Qlibrary, 명시 적 호출을 사용하는 경우이 메서드에는 마이그레이션 파일 헤더가 필요하지 않으며 다음 코드를 참조하십시오.


    QLibrary mylibrary("/home/hsy/SW/Qt5.9.3/Project/build-testso-Desktop_Qt_5_9_3_GCC_64bit-Debug/testso");

    if(!mylibrary.load()){
    
    
        //加载so失败
        qDebug() << "Load Testso.so is failed!";
        qDebug() << mylibrary.errorString();
    }
    //声明函数指针
    typedef QString (*Fun_getName)();
    typedef void (*Fun_testDebug)();

    //resolve得到库中函数地址
    Fun_getName getName = (Fun_getName)mylibrary.resolve("_ZN6Testso7getNameEv");
    Fun_testDebug testDebug = (Fun_testDebug)mylibrary.resolve("_ZN6Testso9testDebugEv");

    if(nullptr == getName){
    
    
       qDebug() << "Load fun() getName failed!";
    }else{
    
    
        ui->label->setText(getName());
    }

    if(nullptr == testDebug){
    
    
        qDebug() << "Load fun() testDebug failed!";
    }else{
    
    
        testDebug();
    }

    //卸载库
    mylibrary.unload();

코드 분석

QLibrary 인스턴스

다음 방법이 일반적으로 사용됩니다.

파일 이름은 생성자를 통해 전달됩니다.이 파일 이름에 대한 공식 권장 사항은 접두사와 접미사를 제거하는 것입니다. 예를 들어 우리 Ubuntu는 Shi에서 생성했습니다 libtestso.so.

우리는 합격 testso이 이름은 호출 된이 될 수 基名접두사와 접미사로, QLibrary당신이에 따라 시스템을 추가하려고합니다. 따라서 기본 이름을 사용하여 작성 有利于跨平台移植됩니다.

여기에 사진 설명 삽입
경우 절대 경로를 사용하지 않는 , 그 QLibrary규칙은 (모든 시스템 라이브러리의 특정 위치를 검색 가고, 접두사와 접미사를 추가하는 것입니다 우분투는 / usr / lib 디렉토리에서 ).
여기에 사진 설명 삽입
따라서 so库시스템 라이브러리로 이동 한 경우 다음 과 같이 작성할 수 있습니다.

QLibrary mylibrary("testso");

절대 경로를 전달할 QLibrary디렉토리로드를 시도하고 파일을 찾을 수없는 경우 플랫폼 별 파일 접두사 또는 접미사에 따라 다시 시도합니다.

또한 setFileName( )명시 적으로 load 2로 설정된 파일 이름 의 인스턴스를 생성 한 후에도 사용할 수 있습니다 .

에서 Ubuntu내가 구덩이를 발견 한 다음 작업을 참조하십시오 so文件:

여기에 사진 설명 삽입
여기에 사진 설명 삽입
libtestso.so에 링크되어 libtestso.so.1.0.0있으며 libtestso.so프로젝트를 라이브러리에 복사해야하는 경우 어떻게됩니까?
여기에 사진 설명 삽입
감정, 당신은 그들이 박 형제라고 생각합니다 ...

여기에 사진 설명 삽입
사실 그들은 섀도우 클론입니다!
여기에 사진 설명 삽입
기분과 Winodw바로 가기의 복사본은 원본 파일과 동일하게 이동하지 않습니다.

해결책 :

  • libtestso.so.1.0.0파일 이름을 다음으로 복사 하고 변경하십시오.libtestso.so
  • 직접 컴파일하고 생성 한 so文件다음so库

여기에 사진 설명 삽입

로드 ()

이 함수는 동적으로로드 된 라이브러리에 사용되며 ,로드가 완료되면로드 isLoaded( )의 성공 여부 load( )를 판단하여 물론 반환 값을 결정하는 것도 가능합니다.

로드에 실패 errorString( )하면 잘못된 정보를 얻을 수 있습니다 .

로드 후 라이브러리는 응용 프로그램이 종료 될 때까지 메모리에 남아 있습니다. unload( )라이브러리 제거 를 시도 있지만 QLibrary다른 인스턴스가 동일한 라이브러리를 사용하는 경우 호출이 실패하고 모든 인스턴스 unload( )3 일 때 unload 를 호출하는 경우에만 호출이 실패 합니다.

해결 ()

QLibrary库일반적인 사용법은 라이브러리에서 내 보낸 심볼을 구문 분석하고 심볼을 호출하는 것입니다 C函数. 이를 "명시 적 링크"라고하며 사용됩니다 resolve( ).

따라서이 함수를 사용하는 것은 함수에 C函数대한 기호 라이브러리에서 파생되어야합니다 resolve(). C ++编译器, 라이브러리를 컴파일하는 데 사용 extern "C"하는 경우 패킹 3 의 함수 블록에서 사용해야합니다 .

음, 경우는 so库사용하지 않는 extern "C"내가, 예를 들어,을 ...

우리는 아는 C++달성 할 수있다 多态규칙으로 컴파일되어 있기 때문에, C다른, C단지와 컴파일러가 생성 기능을 기능 유형 예를 들어 int_add, 및 C++도 가져 컴파일러 생성 기능 매개 변수 유형을 예를 들어 int_add_int_int. 물론 특정 구현은 컴파일러와 관련이 있습니다.

내 생각은 먼저 so库기능을 확인 하는 것입니다 4 , Linux다음 명령을 사용할 수 있습니다.

  • objdump -tT xxx.so
  • nm -D xxx.so

개인적으로 좋아하고 nm여전히 사용할 수 있습니다 awk过滤.

여기에 사진 설명 삽입
따라서 참조 코드에 이상한 함수 이름이 있습니다.

이때 함수를 찾을 수없는 경우이 메서드를 사용 so库하여 원하는 함수가 포함되어 있는지 확인하고 함수 이름이 무엇인지 확인하는 것이 좋습니다 . 결국 컴파일러에 따라 문제가 발생하지 않습니다.

참고 감사합니다


  1. Linux 에서 동적 라이브러리 (.so)와 정적 라이브러리 (.a)의 차이점 ↩︎

  2. 사용 QLibrary 동적 로딩 ↩︎

  3. QLibrary 클래스 ↩︎ ↩︎

  4. Linux보기 동적 라이브러리 .so 내보내기 기능 목록 ↩︎

추천

출처blog.csdn.net/weixin_40774605/article/details/105724118