【转】matlab与C/C++混合编程——在Windows/Linux上调用Matlab编译的动态库文件

转自:matlab与C/C++混合编程——在Windows/Linux上调用Matlab编译的动态库文件_sinat_18131557的博客-CSDN博客

dateversioncomments
2019/9/9V0.1Init
2019/9/27V0.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文件看下:

...
extern LIB_myplot_C_API 
bool MW_CALL_CONV myplotInitialize(void);extern LIB_myplot_C_API 
void MW_CALL_CONV myplotTerminate(void);extern LIB_myplot_C_API bool MW_CALL_CONV mlfMyplot(mxArray* y, mxArray* mytitle);
...

主要是这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到文件夹下直接

sudo ./install

就可以像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输入:

sudo gedit /etc/profile

输入密码后,在打开的文件最后添加:

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。

mcc -W cpplib:myplot -T link:lib myplot.m -c

但是这样生成的代码函数名不一样,参数类型也不一样,需要自己研究研究。

调用.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. -lmyplot

g++使用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权限运行。

sudo -s

添加调试的写入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;

  1. matlab函数编译dll,vs调用该dll的方法, https://blog.csdn.net/a15216111693/article/details/79232288 ↩︎

  2. 填坑VS2017与MATLAB2016b混合编程(生成dll方式), https://blog.csdn.net/qq_20515461/article/details/81229726 ↩︎

  3. mxArray数据类型, https://blog.csdn.net/snowfoxmonitor/article/details/79121178 ↩︎

  4. Linux下c++调用自己编写的matlab函数:通过mcc动态链接库.so实现, https://www.it610.com/article/5486435.htm ↩︎

  5. error adding symbols: DSO missing from command line(在CMakeList中的解决方法), https://blog.csdn.net/lzRush/article/details/84579692 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WinCE中的RAM-Based Registry与HIVE-Based Registry

WinCE支持两种类型注册表&#xff1a;RAM-BasedHIVE-Based&#xff0c;默认使RAM-Based注册表。 1.RAM-Based注册表 RAM-Based注册表所有注册表数据存储象存储(object store)&#xff0c;就存放RAM里面。般有电池备份RAM系统面&#xff0c;就说&#xff0c;当系统掉电以&#…

【转】Linux下c++调用自己编写的matlab函数:通过mcc动态链接库.so实现

转自&#xff1a;Linux下c调用自己编写的matlab函数&#xff1a;通过mcc动态链接库.so实现_Jaster_wisdom的专栏-CSDN博客 之前在这里和这里调用了matlab自带的一些函数&#xff0c;是通过matlab引擎来实现的。那里调用的是matlab自带的函数&#xff0c;那么如果想调用自己写的…

【转】gcc/g++ 链接库的编译与链接

转自&#xff1a;gcc/g 链接库的编译与链接_Surge-CSDN博客_g 链接 gcc/g 链接库的编译与链接 surgewonggmail.com Surge_surgewong_CSDN博客 程序编译一般需要经预处理、编译、汇编和链接几个步骤。在实际应用中&#xff0c;有些公共代码需要反复使用&#xff0c;就把这些代…

常用WebServices返回数据的4种方法比较

以前经常在群里听到朋友们说WebServices的性能特别的慢&#xff0c;说的如何如何。说实话&#xff0c;WebServices的确比调用本地数据要慢一些&#xff0c;可是究竟有多慢&#xff0c;真的如朋友们说的那么难以忍受吗&#xff1f;我个人感觉&#xff0c;多半原因在处理的方式上…

【转】vscode下编译告警“undefined reference”?三步教你如何解决

转自&#xff1a;vscode下编译告警“undefined reference”&#xff1f;三步教你如何解决_squall0984的博客-CSDN博客 近些年来&#xff0c;由于VS Studio体积庞大、价格昂贵等原因&#xff0c;越来越多的C/C开发者转投VSCode的怀抱。VSCode有着免费1、开源2、多平台支持、占…

【转】vscode配置C/C++环境

转自&#xff1a;vscode配置C/C环境 - 知乎 VS Code配置作者&#xff1a;谭九鼎 链接&#xff1a;Visual Studio Code 如何编写运行 C、C 程序&#xff1f; - 知乎 有改动。个人按照步骤后&#xff0c;做到复制上三个json那一步&#xff0c;就可以运行了。 我将settings.json…

【转】dicom网络通讯入门(1)

转自&#xff1a;dicom网络通讯入门&#xff08;1&#xff09; - assassinx - 博客园 如果只看标准就会越看越糊涂&#xff0c;根本原因就是因为dicom抽象得太严重&#xff0c;是“专家”弄的。没办法。 那么到底服务类是什么&#xff1f;sop 又是什么&#xff1f;&#xff0…

用WebORB实现flex + .net后台的Remoting

实现flex与后台通信最简单的方式是采用httpServic的方式&#xff0c;或webservice。但这两种方式都是基于文本的传输&#xff0c;传输效率低&#xff0c;采用RemoteObject的方式&#xff0c;传输的内容采用AMF3格式的二进制编码&#xff0c;效率较高&#xff0c;并且能实现远程…

【转】dicom网络通讯入门(2)

转自&#xff1a;dicom网络通讯入门&#xff08;2&#xff09; - assassinx - 博客园 首先我们现一个echo响应测试工具&#xff0c;也就是echo 的scu&#xff0c;不是实现打印作业管理么。同学我告诉你还早着呢。本来标题取的就是《dicomviewer 第二弹 之 实现打印管理》名字多…

基于WINCE6.0下载multiple XIP镜像文件

备注&#xff1a;基于usb下载的方式&#xff0c;MLC nand flash为K9G8G08U 1. Multiple XIP模式的文件说明 Multiple XIP模式下生成的文件有chain.bin、chain.lst、NK.bin、xip.bin和xipkernel.bin&#xff0c;如下图所示&#xff1a; 图1 2. Eboot下载Multiple XI…

Final Michael Scofield

转载于:https://www.cnblogs.com/andrewx/archive/2009/08/16/1547738.html

【转】dicom网络通讯入门(3)

转自&#xff1a; dicom网络通讯入门&#xff08;3&#xff09; - assassinx - 博客园 接下来可以进行消息传递了 &#xff0c;也就是dimse &#xff0c;再来复习下 什么是dimse 。n-set n-create c-echo 这些都是dimse 他们都是属于一种结构的pdu 那就是tf-pdu&#xff08;传…

【转】ubuntu 下 VNCview 远程桌面无法传输文件问题

转自&#xff1a;ubuntu18.04VNCview文件传输问题_gsls200808的专栏-CSDN博客_vnc传输文件按钮不可用 很多文章说VNCview不能传输文件&#xff0c;实际上这是一个误区。 以ubuntu为例&#xff0c;默认使用 sudo apt-get vnc4server 这个命令安装上的vncserver实际是tigerVNC…

在何时该用什么方式编译WinCE

这是一篇很好的文章&#xff0c;很多开发者其实并没有搞清楚这个问题&#xff1a;在何时该用什么方式编译WinCE 导致走了很多弯路&#xff0c;也包括我自己 感谢作者写了这篇文章 这么好的文章&#xff0c;我想应该翻译过来给大家 在何时该用什么方式编译WinCE 在新闻组里&…

【转】Ubuntu 16.04 安装 CUDA10.1 (解决循环登陆的问题)

转自&#xff1a;Ubuntu 16.04 安装 CUDA10.1 &#xff08;解决循环登陆的问题&#xff09; - Hongkai_Ding - 博客园 0. 前言 这里直接用 cuda安装文件同时安装 NVIDIA 驱动和 CUDA&#xff0c;没有单独安装更高版本的 NVIDIA 驱动&#xff1b;此安装是在 Intel 集显下的图形…

【转】Ubuntu 16.04 Nvidia驱动安装(run方式)

转自&#xff1a;Ubuntu 16.04 Nvidia驱动安装(run方式)_lihe的博客-CSDN博客 1.下载驱动程序 Nvidia驱动下载 https://www.geforce.cn/drivers/beta-legacy http://www.nvidia.cn/Download/index.aspx 根据显卡选择适用的驱动版本&#xff0c;下载完之后是一个名称为 NVIDIA…

2008R2Win7管理九DNS冗余和恢复

2008R2Win7管理九DNS冗余和恢复 本文介绍如何在ad中的主dns崩溃后快速利用备份dns将主dns恢复.以及在主dns完好的时候进行dns冗余备份 本文在file这台服务器上安装好dns角色来作为主dns的备份,安装角色的过程不在详细描述,略过.相信大家都知道了. 本问对拓扑图略有改动,故再次更…

【转】DICOM通讯(ACSE->DIMSE->Worklist)

转自&#xff1a;DICOM通讯&#xff08;ACSE-&#xff1e;DIMSE-&#xff1e;Worklist&#xff09; - 知乎 1 DICOM通讯概要介绍 DICOM通讯和TCP IP的设计原理别无二致。从用户数据到网络上传输数据的过程中&#xff0c;要经历多层协议处理&#xff0c;每经一层&#xff0c;就…

[Silverlight]16进制颜色转ARGB及Color转Int32

16进制色字符串转ARGB做Web的都喜欢用16进制字符串标识颜色&#xff0c;比如“#F5F5F5”这类的&#xff0c;自然这个到了Silverlight/WPF中就要用ARGB表示&#xff1a;“#FFF5F5F5”&#xff08;前两位标识Alpha&#xff0c;即透明度&#xff09;。这在XAML中没有任何问题&…

【转】DICOM通信 - PDU数据包(1)

转自&#xff1a;DICOM通信 - PDU数据包(1)_翼遥君的博客-CSDN博客 DICOM上层协议为DIMSE提供透明的网络数据传输服务&#xff0c;即以上层协议规定的协议数据单元传送接收DIMSE的命令流与数据流。上层协议中制定了上层服务以及协议数据单元(Protocol Data Unit&#xff0c;PD…