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文件。項目中遇到過類似的東西,拿出來備忘和共享