【转】matlab与C/C++混合编程——在Windows/Linux上调用Matlab编译的动态库文件
轉自:matlab與C/C++混合編程——在Windows/Linux上調用Matlab編譯的動態庫文件_sinat_18131557的博客-CSDN博客
| 2019/9/9 | V0.1 | Init |
| 2019/9/27 | V0.2 | 添加報錯信息寫入log的實現 |
文章目錄
-
-
- MATLAB生成Dll文件調用
-
-
- 生成dll文件
- 調用dll文件
-
- MATLAB生成.so文件調用
-
-
- Linux安裝Matlab
- 生成.so文件
- 調用.so在Linux實現matlab代碼內容
- 添加調試的寫入log文件的功能
-
-
主要是想使用MATLAB的m文件,生成可以使用C/C++調用的文件,兩種方式
- 生成dll文件,在Windows下調用,使用VS構建調用的工程
- 生成so文件在Linux下調用,調用代碼使用g++編譯
MATLAB生成Dll文件調用
生成dll文件
在Windows上安裝Matlab過程略,在Matlab中創建函數:
function myplot(y,mytitle) %UNTITLED 此處顯示有關此函數的摘要plot(y);title(mytitle);saveas(gcf,'myplot.jpg'); end這樣寫的好處在于傳遞了數組類型,還傳遞了字符串類型,使用了繪圖功能,能全面看下變量怎么傳遞的。
然后在APP欄目下找到Library Compiler進去,或者在命令行中輸入deploytool1:
得到如下界面:
上方選擇C/C++ Shared Library,EXPORTED FUNCTIONS的加號點一下,選中要輸出的文件,Library Name可以更加需要選擇改或者不改,另外這里只有一個文件需要轉化,如果這個文件中調用了另外一個自己寫的函數中的內容,需要把這個函數放在Files required for your library to run中,當右邊的Package變為綠色就可以點擊Package,選擇保存目錄,等待一會讓就好了。完成后會得到三個文件,選擇for_redistribution_files_only進入就有了如下文件:
調用dll文件
打開vs創建一個新的C++的控制臺工程。第一步將解算方案平臺改為x64,不然后面可能會報錯:
然后配置2:
項目-> 項目屬性-> VC++目錄->包含目錄添加…\MATLAB\R2016a\extern\include
庫目錄添加…C:\Program Files\MATLAB\R2016a\extern\lib\win64\microsoft
在鏈接器里面添加這幾項(這里只用到了myplot.lib 和mclmcrrt.lib,資料上其他應用有用到其他幾個):
myplot.lib (自己的lib文件)
mclmcr.lib (以下都是matlab的文件)
mclmcrrt.lib
libmx.lib
libmex.lib
libmat.lib
libeng.lib
把Matlab生成的.dll,.h,.lib文件復制到項目的源文件的目錄下(放到存放cpp文件目錄里面),在項目里包含.h文件。首先打開.h文件看下:
主要是這3個函數是需要使用的。
- myplotInitialize 初始化
- myplotTerminate 關閉函數
- mlfMyplot(mxArray* y, mxArray* mytitle) 運行函數
由于C/C++沒有mxArray*這樣的數據類型,需要進行數據轉化,y是數組(一維矩陣),mytitle是個字符串 所以可以用這樣的方式創建,(可參考3):
mxArray *dest_ptr =mxCreateDoubleMatrix(rows,cols, mxREAL); mxArray* mytitle = mxCreateString(const char *str)同時含有必要的初始化,調用代碼如下:
// ConsoleApplication1.cpp : 定義控制臺應用程序的入口點。 //#include "stdafx.h" #include "myplot.h" #include "string.h"int main() {// 初始化mclmcr,如果沒有這兩句可能會有訪問錯誤的問題mclmcrInitialize();if (!mclInitializeApplication(NULL, 0)){return -1;}printf("mclmcr Initialized!\n");//初始化應用if (!myplotInitialize()){return -1;}printf("myplot Initialized!\n");// 初始化數據并做數據轉化double data[5] = { 1,2,3,4,5 };mxArray *y = mxCreateDoubleMatrix(5, 1, mxREAL);memcpy((void*)mxGetPr(y), (void*)data, sizeof(data));const char *title = "mytile";mxArray* mytitle = mxCreateString(title);printf("data Initialized!\n");//調用主函數mlfMyplot(y, mytitle);// 關閉應用myplotTerminate();system("pause");return 0; }如果發生這樣的異常:
- mclmcr一定要初始化mclmcrInitialize();if (!mclInitializeApplication(NULL, 0)){return -1;}
- 按Ctrl+Alt+E將Win32 Exceptions的異常取消勾選。
如果沒有異常,能在工程目錄下看到myplot.jpg文件。
MATLAB生成.so文件調用
生成.so文件需要在Linux環境下安裝MATLAB。
Linux安裝Matlab
Matlab下載地址:https://ww2.mathworks.cn/downloads/web_downloads/?s_iid=hp_ff_t_downloads
在Linux下盡量選擇MATLAB版本與MATLAB Runtime的版本一致,如都是2019a,不然可能在編譯時候或者編譯以后有libstdc++.so的版本錯誤問題,選擇版本以后可以直接選擇Linux系統進行下載,我使用的學校郵箱注冊,可以直接使用學術license。下載完成,解壓出來后,cd到文件夾下直接
就可以像Windows上安裝軟件一樣點下一步下一步了。
如果只需要跑代碼,不用這個寫/調試的機器,只需要安裝Matlab Runtime就可以了。在官網下載,安裝方式同Matlab一樣,使用sudo ./install命令就可以,安裝完成以后,提示將幾個變量添加到環境變量里面去:
我的是這樣的:
/usr/local/MATLAB/MATLAB_Runtime/v96/runtime/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v96/bin/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v96/sys/os/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v96/extern/bin/glnxa64,不同機器與版本,這個路徑不一樣,按照顯示的添加就行。
使用terminal輸入:
輸入密碼后,在打開的文件最后添加:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/MATLAB/MATLAB_Runtime/v93/runtime/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v93/bin/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v93/sys/os/glnxa64由于libmyplot.h文件中還包含了一個mclmcrrt.h文件,使用g++編譯時候,還需要把這個文件的路徑添加到CPLUS_INCLUDE_PATH中,所以還要打開/etc/profile文件在最后添加一行:
export CPLUS_INCLUDE_PATH=/usr/local/MATLAB/MATLAB_Runtime/v96/extern/include:$CPLUS_INCLUDE_PATH這個文件在…/extern/include里面,前面的路徑是安裝地址。
到這里,Linux安裝完成~
生成.so文件
生成.so的文件必須要為函數,不能是腳本,假設現在需要轉化的函數為:
function myplot(y,mytitle) %UNTITLED 此處顯示有關此函數的摘要plot(y);title(mytitle);saveas(gcf,'myplot.jpg'); end就是把輸入的y通過plot繪圖出來,并且,這個圖的title是mytitle這個字符串。寫成這樣是為了說明2種數據類型的轉化關系。
在matlab的APP欄中,找到Library Compiler進去,或者在命令行中輸入:deploytool然后選擇Library Compiler進入到如下界面:
上方選擇C/C++ Shared Library,EXPORTED FUNCTIONS的加號點一下,選中要輸出的文件,Library Name可以更加需要選擇改或者不改,當右邊的Package變為綠色就可以了。
點擊Package。選擇保存工程的文件夾,等待一會兒就好了。打開所在目錄得到了:
打開for_redistribution_files_only就可以看到.h和.so文件了。
另外也可在matlab中使用mcc命令生成.so文件4。
但是這樣生成的代碼函數名不一樣,參數類型也不一樣,需要自己研究研究。
調用.so在Linux實現matlab代碼內容
同樣地,寫一個cpp文件:
// ConsoleApplication1.cpp : 定義控制臺應用程序的入口點。 //#include "myplot.h" #include "string.h"int main() {// 初始化mclmcr,如果沒有這兩句可能會有訪問錯誤的問題mclmcrInitialize();if (!mclInitializeApplication(NULL, 0)){return -1;}printf("mclmcr Initialized!\n");//初始化應用if (!libmyplotInitialize()){return -1;}printf("myplot Initialized!\n");// 初始化數據并做數據轉化double data[5] = { 1,2,3,4,5 };mxArray *y = mxCreateDoubleMatrix(5, 1, mxREAL);memcpy((void*)mxGetPr(y), (void*)data, sizeof(data));const char *title = "mytile";mxArray* mytitle = mxCreateString(title);printf("data Initialized!\n");//調用主函數mlfMyplot(y, mytitle);// 關閉應用libmyplotTerminate();return 0; }要注意初始化應用,關閉應用,主程序的函數名,在libmyplot.h文件中能找到對應的函數名字。
在打開一個terminal,輸入使用g++編譯的指令:
g++ sotest.cpp -o sotest -L. -lmyplotg++使用g++編譯方式編譯,sotest.cpp編譯的源文件為sotest.cpp,-o sotest輸出sotest文件作為可執行文件,-L. -lmyplot指定需要鏈接的庫,也就是當前文件目錄下的libmyplot.so文件。按理說能成功,結果:
/usr/bin/ld: /tmp/ccXUI6xA.o: undefined reference to symbol 'mxCreateDoubleMatrix_800_proxy' /usr/local/MATLAB/MATLAB_Runtime/v96/runtime/glnxa64/libmwmclmcrrt.so.9.6: error adding symbols: DSO missing from command line collect2: error: ld returned 1 exit status網上找資料定位到error adding symbols: DSO missing from command line這個問題是libmwmclmcrrt.so.9.6文件沒找到,可是已經添加了LD_LIBRARY_PATH,有點不明所以,既然你說你沒找到,那就給你指定文件吧5:
g++ sotest.cpp -o sotest -L. -lmyplot -L/usr/local/MATLAB/R2019a/runtime/glnxa64/ -lmwmclmcrrt運行成功,沒有報錯,但是也什么都沒有輸出,這個時候能看到多了一個sotest文件:
這時候運行sotest文件能得到結果:
如果libmyplotInitialize()初始化失敗的話,切換到sudo權限運行。
添加調試的寫入log文件的功能
由于在Linux上跑,通常需要記錄下出錯的問題,把錯誤信息寫到log文件中。寫了一個寫入log文件的函數,每一個月的信息存放一個文件,如果文件不存在,就創建一個:
#include "time.h" #include <fstream> #include <iostream>void WriteLog(const char* text) {char LOG_FILE[1024]={0};// get the time nowtime_t now=time(0);tm *ltm = localtime(&now);snprintf(LOG_FILE,1024,"./log/log_%04d%02d.txt", ltm->tm_year+1900,ltm->tm_mon+1);// if LOG file not exist, then create it!fstream f1;f1.open(LOG_FILE,ios::in);if(!f1){f1.close();f1.open(LOG_FILE,ios::out);f1.close();}// write info in log file!FILE *fp;fp=fopen(LOG_FILE, "a+");if (fp != NULL){char ltime[1024] = { 0 };strftime(ltime,1024,"%Y-%m-%d %H:%M:%S ", ltm); fwrite(ltime, sizeof(char), strlen(ltime), fp);fwrite(text, sizeof(char), strlen(text), fp);fwrite("\r\n", sizeof(char), 2, fp);fclose(fp);printf("%s\n",text);}}并且,有參數傳進來的話,也希望把出錯時候的參數保存下來,所以主函數需要接受參數:
int main(int argc, char **argv) {... }argc表示參數的個數,argv表示參數,其中argv[0]是命令本身,從1開始表示參數。把參數信息整合出來:
void formatLogInfo(int argc, char **argv, char * info) {for (int i=1;i<argc;i++){char tmp[1024]={0};snprintf(tmp,1024,"%s parameter[%d]=%s",info,i,argv[i]);snprintf(info,1024,"%s",tmp);} }所以最終的運行CPP文件:
#include "myplot.h" #include "string.h"#include "time.h" #include <fstream> #include <iostream>using namespace std;void WriteLog(const char* text) {char LOG_FILE[1024]={0};// get the time nowtime_t now=time(0);tm *ltm = localtime(&now);snprintf(LOG_FILE,1024,"./log/log_%04d%02d.txt", ltm->tm_year+1900,ltm->tm_mon+1);// if LOG file not exist, then create it!fstream f1;f1.open(LOG_FILE,ios::in);if(!f1){f1.close();f1.open(LOG_FILE,ios::out);f1.close();}// write info in log file!FILE *fp;fp=fopen(LOG_FILE, "a+");if (fp != NULL){char ltime[1024] = { 0 };strftime(ltime,1024,"%Y-%m-%d %H:%M:%S ", ltm); fwrite(ltime, sizeof(char), strlen(ltime), fp);fwrite(text, sizeof(char), strlen(text), fp);fwrite("\r\n", sizeof(char), 2, fp);fclose(fp);printf("%s\n",text);}}void formatLogInfo(int argc, char **argv, char * info) {for (int i=1;i<argc;i++){char tmp[1024]={0};snprintf(tmp,1024,"%s parameter[%d]=%s",info,i,argv[i]);snprintf(info,1024,"%s",tmp);} }int main(int argc, char **argv) {// 初始化mclmcr,如果沒有這兩句可能會有訪問錯誤的問題mclmcrInitialize();if (!mclInitializeApplication(NULL, 0)){char info[1024]="mclInitializeApplication failed!";formatLogInfo(argc,argv,info);WriteLog(info);return -1;}printf("mclmcr Initialized!\n");//初始化應用if (!libmyplotInitialize()){char info[1024]="libmyplotInitialize failed!";formatLogInfo(argc,argv,info);WriteLog(info);return -2;}printf("myplot Initialized!\n");// 初始化數據并做數據轉化double data[5] = { 1,2,3,4,5 };mxArray *y = mxCreateDoubleMatrix(5, 1, mxREAL);memcpy((void*)mxGetPr(y), (void*)data, sizeof(data));const char *title = "mytile";mxArray* mytitle = mxCreateString(title);printf("data Initialized!\n");//調用主函數if(!mlfMyplot(y, mytitle)){char info[1024]="mlfMyplot function failed! Please check the parameter(s)!";formatLogInfo(argc,argv,info);WriteLog(info);return -3;}// 關閉應用libmyplotTerminate();return 0;matlab函數編譯dll,vs調用該dll的方法, https://blog.csdn.net/a15216111693/article/details/79232288???
填坑VS2017與MATLAB2016b混合編程(生成dll方式), https://blog.csdn.net/qq_20515461/article/details/81229726???
mxArray數據類型, https://blog.csdn.net/snowfoxmonitor/article/details/79121178???
Linux下c++調用自己編寫的matlab函數:通過mcc動態鏈接庫.so實現, https://www.it610.com/article/5486435.htm???
error adding symbols: DSO missing from command line(在CMakeList中的解決方法), https://blog.csdn.net/lzRush/article/details/84579692???
總結
以上是生活随笔為你收集整理的【转】matlab与C/C++混合编程——在Windows/Linux上调用Matlab编译的动态库文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5月轿车销量排名出炉:德系“反攻” 比亚
- 下一篇: 5月MPV销量排名出炉:销冠还是销冠 老