https://zhuanlan.zhihu.com/p/270265066
https://doc.qt.io/qt-5/qtserialbus-socketcan-overview.html
https://www.coder.work/article/174502
http://www.qtcn.org/bbs/simple/?t24903.html
https://bbs.csdn.net/topics/391930574
https://qtguide.ustclug.org/ch07-02.htm
https://www.jianshu.com/p/317cf4a98c77
https://www.jianshu.com/p/15e681a0fdcb
https://developer.aliyun.com/article/114654
http://www.emtronix.com/article/zhishi2014426.html
https://www.codenong.com/js15e681a0fdcb/
轉載鏈接:https://blog.csdn.net/chenjinlong126/article/details/78990350
https://m.xp.cn/b.php/79361.html
https://my.oschina.net/u/4228078/blog/3119351
推測除了so動態庫的方式,應該還有另外一種方式,即g++和gcc分別編譯C++和c文件,生成目標文件o,最后鏈接到一起。
C++調用C語言編譯的so文件
一.制作so文件:libadd_c.so或libadd_cpp.so
1、add.c:
int add(int a, int b) { return a + b; }
編譯:
gcc -shared -fpic -lm -ldl -o libadd_c.so add.c
2、add.cpp:
extern "C" { int add(int a, int b) { return a + b; } }
編譯:
gcc -shared -fpic -lm -ldl -o libadd_cpp.so add.cpp
2.編寫測試函數
test.cpp:
#include <dlfcn.h>
#include <stdlib.h>
#include <iostream>
{
int a = 0;
{
printf("open lib error\n");
cout<<dlerror()<<endl;
return -1;
}
{
cout<<dlerror()<<endl;
dlclose(handle);
return -1;
}
printf("a = %d\n",a);
dlclose(handle);
return 0;
}
編譯:
g++ test.cpp -ldl -o test
3.運行
./test
參考:
介紹一下上面用到的接口函數
1) dlopen
函數原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必須在dlerror,dlsym和dlclose之前調用,表示要將庫裝載到內存,准備使用。如果要裝載的庫依賴於其它庫,必須首先裝載依賴庫。如果dlopen操作失敗,返回NULL值;如果庫已經被裝載過,則dlopen會返回同樣的句柄。
參數中的libname一般是庫的全路徑,這樣dlopen會直接裝載該文件;如果只是指定了庫名稱,在dlopen會按照下面的機制去搜尋:
a.根據環境變量LD_LIBRARY_PATH查找
b.根據/etc/ld.so.cache查找
c.查找依次在/lib和/usr/lib目錄查找。
flag參數表示處理未定義函數的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去處理未定義函數,先把庫裝載到內存,等用到沒定義的函數再說;RTLD_NOW表示馬上檢查是否存在未定義的函數,若存在,則dlopen以失敗告終。
2) dlerror
函數原型:char *dlerror(void);
功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤信息,返回NULL表示無錯誤。dlerror在返回錯誤信息的同時,也會清除錯誤信息。
3) dlsym
函數原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen之后,庫被裝載到內存。dlsym可以獲得指定函數(symbol)在內存中的位置(指針)。如果找不到指定函數,則dlsym會返回NULL值。但判斷函數是否存在最好的方法是使用dlerror函數,
4) dlclose
函數原型:int dlclose(void *);
功能描述:將已經裝載的庫句柄減一,如果句柄減至零,則該庫會被卸載。如果存在析構函數,則在dlclose之后,析構函數會被調用。
好了,現在來編譯打包,命令如下:
$ g++ -shared -fPIC -o libhello.so hello.cpp
$ g++ main.cpp -ldl
在上面dlopen函數中,看到我們傳的第一個參數並沒有指定路徑,只給出了庫的名稱。那是因為已經在環境變量LD_LIBRARY_PATH中指定了 ./ 目錄。如果你想放在其他目錄,修改該環境變量即可。
在C語言中調用C++做的動態鏈接庫
今天在做東西的時候遇到一個問題,就是如何在C語言中調用C++做的動態鏈接庫so文件
如果你有一個c++做的動態鏈接庫.so文件,而你只有一些相關類的聲明, 那么你如何用c調用呢,別着急,本文通過一個小小的例子,讓你能夠很爽的搞定.
鏈接庫頭文件:
head.h
class A
{
public:
A();
virtual ~A();
int gt();
int pt();
private:
int s;
};
firstso.cpp
#include <iostream>
#include "head.h"
A::A(){}
A::~A(){}
int A::gt()
{
s=10;
}
int A::pt()
{
std::cout<<s<<std::endl;
}
編譯命令如下:
g++ -shared -o libmy.so firstso.cpp
這時候生成libmy.so文件,將其拷貝到系統庫里面:/usr/lib/
進行二次封裝:
secso.cpp
#include <iostream>
#include "head.h"
extern "C"
{
int f();
int f()
{
A a;
a.gt();
a.pt();
return 0;
}
}
編譯命令:
gcc -shared -o sec.so secso.cpp -L. -lmy
這時候生成第二個.so文件,此時庫從一個類變成了一個c的接口.
拷貝到/usr/lib
下面開始調用:
test.c
#include "stdio.h"
#include "dlfcn.h"
#define SOFILE "sec.so"
int (*f)();
int main()
{
void *dp;
dp=dlopen(SOFILE,RTLD_LAZY);
f=dlsym(dp,"f");
f();
return 0;
}
編譯命令如下:
gcc -rdynamic -s -o myapp test.c
運行Z$./myapp
10
$
C調用C++的動態庫
假設C++動態庫中一個函數void world()函數需要被C使用到,同時C++動態庫的名稱為world.so,可以編寫一個臨時cpp程序,假設名稱為mid.cpp,內容如下:
#include<iostream>
void m_world()
{
world();
}
在mid.h文件中:
extern void world();
#ifdef __cplusplus
extern “C” {
#endif
void m_world();
#ifdef __cplusplus
}
#endif
編譯成新的動態庫:
g++ --shared -o libmid.so mid.cpp -lworld
C語言可以通過使用m_world()函數間接調用world()函數。
PS.
#ifdef __cplusplus當使用c++編譯器的時候會成立;
extern “c” {}含義是括號中的函數使用C編譯器編譯。