IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【转】matlab与C/C++混合编程——在Windows/Linux上调用Matlab编译的动态库文件 -> 正文阅读

[C++知识库]【转】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的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 now
	time_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 now
	time_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???

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-19 07:48:45  更:2021-09-19 07:51:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 21:50:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码