Linux系統下Qt調用c++編譯的庫

Linux系統下Qt調用c++編譯的庫

Linxu系統下調用c語言編譯的so文件流程比較簡單,代碼流程如下(不做介紹了,可以查看qt assistant)


typedef int (*fun)();
typedef void (*Fun)(char*);

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

    QLibrary mylib("libhelloqt.so");

    if (mylib.load())
    {
        ui->label->setText("so is loaded!!");

        fun open1=(fun)mylib.resolve("getInt");

        if (open1)
        {
            ui->label_2->setText(QString("data coming"));
            int result;
            result = open1();
            qDebug("lib data is %d", result);


        }else
        {
            ui->label_2->setText(QString("data missing"));
        }

        Fun open2=(Fun)mylib.resolve("getString");

        if (open2)
        {
            char buf[128];
            open2(buf);
            qDebug("lib data is %s", buf);

        }

    }else
    {
        ui->label->setText("so is not loaded!!");
    }

}


着重記錄一下QT調用C++代碼編譯生成的庫


有可能產生錯誤的地方下:(紅色內容引用自:http://blog.163.com/onx0320@126/blog/static/16473643620126120361030/)



有時你想在運行時加載一個庫(並使用其中的函數),這在你爲你的程序寫一些插件或模塊架構的時候經常發生。
在C語言中,加載一個庫輕而易舉(調用dlopen、dlsym和dlclose就夠了),但對C++來說,情況稍微複雜。動態加載一個C++庫的困難一 部分是因爲C++的name mangling(譯者注:也有人把它翻譯爲“名字毀壞”,我覺得還是不翻譯好),另一部分是因爲dlopen API是用C語言實現的,因而沒有提供一個合適的方式來裝載類。
在解釋如何裝載C++庫之前,最好再詳細瞭解一下name mangling。我推薦您瞭解一下它,即使您對它不感興趣。因爲這有助於您理解問題是如何產生的,如何才能解決它們。

  Name Mangling
在每個C++程序(或庫、目標文件)中,所有非靜態(non-static)函數在二進制文件中都是以“符號(symbol)”形式出現的。這些符號都是唯一的字符串,從而把各個函數在程序、庫、目標文件中區分開來。
在C中,符號名正是函數名:strcpy函數的符號名就是“strcpy”,等等。這可能是因爲兩個非靜態函數的名字一定各不相同的緣故。
而C++允許重載(不同的函數有相同的名字但不同的參數),並且有很多C所沒有的特性──比如類、成員函數、異常說明──幾乎不可能直接用函數名作符號 名。爲了解決這個問題,C++採用了所謂的name mangling。它把函數名和一些信息(如參數數量和大小)雜糅在一起,改造成奇形怪狀,只有編譯器才懂的符號名。例如,被mangle後的foo可能 看起來像foo@4%6^,或者,符號名裏頭甚至不包括“foo”。
其中一個問題是,C++標準(目前是[ISO14882])並沒有定義名字必須如何被mangle,所以每個編譯器都按自己的方式來進行name mangling。有些編譯器甚至在不同版本間更換mangling算法(尤其是g++ 2.x和3.x)。即使您搞清楚了您的編譯器到底怎麼進行mangling的,從而可以用dlsym調用函數了,但可能僅僅限於您手頭的這個編譯器而已, 而無法在下一版編譯器下工作。

  類
使用dlopen API的另一個問題是,它只支持加載函數。但在C++中,您可能要用到庫中的一個類,而這需要創建該類的一個實例,這不容易做到。

解決方案

extern "C"
C++有個特定的關鍵字用來聲明採用C binding的函數:extern "C" 。 用 extern "C"聲明的函數將使用函數名作符號名,就像C函數一樣。因此,只有非成員函數才能被聲明爲extern "C",並且不能被重載。儘管限制多多,extern "C"函數還是非常有用,因爲它們可以象C函數一樣被dlopen動態加載。冠以extern "C"限定符後,並不意味着函數中無法使用C++代碼了,相反,它仍然是一個完全的C++函數,可以使用任何C++特性和各種類型的參數。

加載函數
在C++中,函數用dlsym加載,就像C中一樣。不過,該函數要用extern "C"限定符聲明以防止其符號名被mangle。



下面提供一個我調試時的例子,解決Qt 在linux下調用C++庫的流程


編譯庫的源文件包含如下:hello.cpp  hi.cpp  hi.h


hi.h源碼如下:

#include <string.h>

class Hi
{
public:
        virtual int getInt() = 0;
        virtual void getString(char* str) = 0;
private:
        int a;
};

hi.cpp源碼如下:

#include "hi.h"



typedef Hi* createHi();
typedef void destroyHi(Hi*);

hello.cpp源碼如下:


#include "hi.h"

class Hello: public Hi{
public:
        virtual int getInt()
        {
                return 128;
        }

        virtual void getString(char* str)
        {
                strcat(str, "C++ So : Agricaultrual  Bank Of China!");
        }
};

extern "C" Hi* createHi()
{
        return new Hello();
}

extern "C" void destroyHi(Hi* p)
{
        delete p;
}


接下來編譯so.   寫個簡單的makefile即可


最後Qt調用,調用方式如下:


#include "CallCppsoDialog.h"
#include "ui_CallCppsoDialog.h"

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


    QLibrary myLib("libhellocpp.so");
    ui->label->setText("bf myLib load");
    if (myLib.load())
    {

        ui->label->setText("myLib.load");

        Hi* myHi;

         typedef Hi* (*MyPrototype)();
         MyPrototype myFunction = (MyPrototype) myLib.resolve("createHi");

         if (!myFunction)
         {
             qDebug("Null Hi");
             return;
         }


         myHi = myFunction();
         qDebug("--------%d---------", myHi->getInt());

         char buffer[128] = {0};
         myHi->getString(buffer);

         ui->label_2->setText(QString(buffer));

         typedef void (*Fun)(Hi* p);
         Fun fun = (Fun)myLib.resolve("destroyHi");
         if (fun)
         {
             fun(myHi);
         }

         myLib.unload();

    }

}

CallCppSoDialog::~CallCppSoDialog()
{
    delete ui;
}


利用C++的多態 和  extern "C" 可以完美解決Qt在調用C++文件編譯的so文件。項目中遇到過類似的東西,拿出來備忘和共享


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章