前言mwArrayBrief构造函数成员方法C++ 调用 MATLAB 实例前期准备无返回值有返回值注意事项
之前我写过一篇 MATLAB与C语言的混合编程,那个是用 mex 命令 来编译 .C 文件 生成 .mexw64 文件来给 MATLAB 调用。注意啊,是在 MATLAB中 调用 C函数。
现在,在这偏推送里讲的是:在 C++中 调用 MATLAB函数 !
其实,我之前也没怎么学在C++中调用MATLAB,突然学这个是因为 上上周老师催着要一个原型发给客户。但是核心的算法目前仅仅是用MATLAB验证了正确性,还没有用C++写。如果核心的算法用 C++ 来写的话估计要花好久(毕竟也就学了一个学期多点的C++)。所以为了赶时间,我就打算用 MATLAB 把核心的算法打包成动态,然后拿去给C++调用。
简单展示下还没做完的东西吧:
不多说废话了,进入正题。
关于 mwArray 的构造函数 以及 其成员方法 部分,完全是按照官网上的 文档复制过来的,有一些关于稀疏矩阵的方法我这里没放,我平时也没解过大型的方程组,所以稀疏矩阵这块也没怎么用,也就没有花时间去了解它。
Class used to pass input/output arguments to C++ functions generated by MATLAB Compiler SDK
用于将输入/输出参数传递给由MATLAB Compiler SDK生成的C ++函数的类。
使用mwArray类将输入/输出参数传递给由MATLAB生成的 C++ 接口函数。在MATLAB中所有的数据均由数组表示,这个类就是是对MATLAB中数组的封装。mwArray类提供必一些构造函数,方法以及运算符,用于数组的创建和初始化 以及 简单索引方式取值。
1. 创建 空的 数组
/// 空的 数组,元素类型预定为 double; 构造函数:mwArray(mxClassID mxID)
2. 二维数组
/// 二维数组 构造函数:mwArray(mwSize num_rows, mwSize num_cols, mxClassID mxID, mxComplexity cmplx = mxREAL)
3. 三维数组/多维数组
/// 三维数组 mwArray(mwSize num_dims, const mwSize* dims, mxClassID mxID, mxComplexity cmplx = mxREAL)
其实 二维数组 也可以用这样的方式来创建
4. 一维字符数组(字符串)
/// 一维字符数组 mwArray(const char* str)
5. 多行字符串
/// 多行字符串,不要求每行字符个数相等 mwArray(mwSize num_strings,const char ** str)
6. 结构体数组
/// 二维 结构体数组 mwArray(mwSize num_rows, mwSize num_cols, int num_fields, const char** fieldnames)
这个可能稍微难理解一点,多看几眼这个构造函数,这个构造函数就是说明结构体数组有 几行 几列,几个域名,每个域名具体是什么。对于结构体值的设置,看一下下面的第30个成员方法。
7. 多维结构体数组
略,我都没碰见过,就不写这个了。
8. 拷贝构造函数(深拷贝)
mwArray(const mwArray& arr)
9. 标量
/// 标量 实数:mwArray( re);虚数:mwArray( re, im)
基本复制的官网上的,基本都没改。
1. 深拷贝 mwArray Clone() const
mwArray a(2, 2, mxDOUBLE_CLASS);
2. 浅拷贝(公用同一个地址的值)mwArray SharedCopy() const
mwArray a(2, 2, mxDOUBLE_CLASS);
3. 把数组 序列化 为 字节。mwArray Serialize() const
mwArray a(2, 2, mxDOUBLE_CLASS);
反序列化成原始形势:mwArray::Deserialize().
4. 确定数组的类型 mxClassID ClassID() const
mwArray a(2, 2, mxDOUBLE_CLASS);
5. 确定数组类型元素的大小(以字节为单位)size_t ElementSize()const
mwArray a(2, 2, mxDOUBLE_CLASS);
6. 确定数组元素的个数 mwSize NumberOfElements() const
mwArray a(2, 2, mxDOUBLE_CLASS);
7. 确定数组的维数 mwSize NumberOfDimensions() const
mwArray a(2, 2, mxDOUBLE_CLASS);
8. 确定结构体数组中的字段数(如果这个mwArray变量不是结构体类型则返回0)int NumberOfFields() const
const
9. 获取 结构体的 第 index 个字段名 mwString GetFieldName(int index)
;如果mwArray变量不是结构体 则抛出异常
const
10. 返回数组每个维度的大小(比如 几行几列):mwArray GetDimensions() const
。返回的其实也是数组
mwArray a(2, 2, mxDOUBLE_CLASS);
11. 判断数组是否为空 bool IsEmpty() const
mwArray a;
bool b = a.IsEmpty();
std::cout <"11.IsEmpty(): "
12. 判断是否是 数值数组 bool IsNumeric() const
mwArray a(2, 2, mxDOUBLE_CLASS);
13. 判断是否是 复数 数组 bool IsComplex() const
mwArray a(2, 2, mxDOUBLE_CLASS, mxCOMPLEX);
14. 判断两个数组是否相等(逐字节判断)bool Equals(const mwArray& arr) const
mwArray a(2, 2, mxDOUBLE_CLASS);
所以就算你用相同的数据来初始化两个不同类型的数组(如mxINT_CLASS 和 mxDOUBLE_CLASS),其结果也可能是不相等。
也就是这个方法只能用于两个像同类型的数组来判断是否相等
15. 把mwArray数组转化成字符串输出 mwString ToString()const
mwArray a(2, 2, mxDOUBLE_CLASS, mxCOMPLEX);
16. 把一个 实数矩阵 转化为 复数矩阵 void MakeComplex()
;原地修改;如果不是数值矩阵会抛出异常
double rdata[
17. 根据索引获取数组的中的值 mwArray Get(mwSize num_indices, …)
double data[
Note: 索引从 1 开始!,第一个参数可以是数组的维度,紧接着后面的系列参数表示 取 第几行第几列 或者 第几页的元素值。以二维数组为例,第一个参数正常写成 2 ,后面两个参数分别代表 行 和 列。
如果第一个参数是 1,则应该只输入两个参数才对。假设第2个参数是 n , 那么将按照列来数 第 n 个元素值
18. 根据 域名 和 索引 获取结构体数组中 第 索引 处的 域名下的值 mwArray Get(const char* name, mwSize num_indices, …)
const
Note:后面索引的问题,同上
19. 获取数组的 实部 和 虚部 mwArray Real()
;mwArray Imag()
double rdata[
20. 用一个mwArray数组的值来赋值给另一个mwArray(用的同一个地址上的值);void Set(const mwArray& arr)
mwArray a(2, 2, mxDOUBLE_CLASS);
21. 从mwArray 中拷贝 len 个数值到 一个C标准数组中 void GetData(* buffer, mwSize len) const
double rdata[
22. 从 mwArray 字符数组中拷贝 len 个 字符 到一个 char[] 数组中 void GetCharData(mxChar* buffer, mwSize len) const
6] = {
23. 给mwArray 数组赋值,从一个 标准 C 数组中 拷贝前 len 个数值 到 mwArray 数组中 void SetData(* buffer, mwSize len)
double rdata[
24. 给逻辑数组赋值 void SetLogicalData(mxLogical* buffer, mwSize len)
4] = {
25. 给mwArray字符数组赋值 void SetCharData(mxChar* buffer, mwSize len)
6] = {
26. 判断数值是否有限
static
27. 判断数值是否无穷
static
28. 判断数值是否 NaN
static
29. mwArray operator()(mwIndex i1, mwIndex i2, mwIndex i3, …, )
double data[
和MATLAB中一样的获取值的方法
30. mwArray operator()(const char* name, mwIndex i1, mwIndex i2, mwIndex i3, …, )
const
Note:获取结构体的值
31. 给mwArray数组中的某个元素设置某个标量 mwArray& operator=(const& x)
mwArray a(2, 2, mxDOUBLE_CLASS);
这里只是举了两个简单的例子,通过MATLAB来画图。
首先设置MATLAB的编译器 主要设置C++编译器,装好 VS2017,然后
mbuild -setup
分别点下那两个箭头,然后就好了。如果你用的MinGW编译器则配置起来多几步,这里就不多说了,Win中还是最好用VS的编译器吧。
还有就是你需要引入 MATLAB 的库,和 MCR 那些相关的,之前在 搭建VS2017_QT_MATLAB开发环境 这偏推送种写过,习惯用VS的同学可以看这篇推送去。不过,我现在用的是 CLion ,用 CMake 来引入 MATLAB 的库,相关的 CMakeLists.txt 的写法如下
###################################### MATLAB库 ######################################
# 把包含 MATLAB 那6个lib所在的头文件的目录进来(固定的)
include_directories("C:/Program Files/Polyspace/R2019a/extern/include")
include_directories("C:/Program Files/Polyspace/R2019a/extern/include/win64")
# 把我们用 MATLAB 生成的那些lib的头文件的目录包含进来(我就放在当前目录下)
INCLUDE_DIRECTORIES("./")
# 把我们用 MATLAB 生成的那些 lib所在的 目录 链接 进来(我也放在当前目录下)
link_directories(./)
# 把我们用MATLAB生成的那些 lib 所 依赖的 MATLAB的库目录路径 链接进来
link_directories("C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft")
# 链接 我们用MATLAB生成的那个lib 所依赖的 6个库文件 链接程序中,供那个库使用
link_libraries(
"C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft/libmex.lib"
"C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft/libmx.lib"
"C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft/libmat.lib"
"C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft/libeng.lib"
"C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft/mclmcr.lib"
"C:/Program Files/Polyspace/R2019a/extern/lib/win64/microsoft/mclmcrrt.lib"
)
add_executable(target main.cpp)
target_link_libraries(target PUBLIC 生成的MATLAB库.lib)
Note:如果生成的 MATLAB库对应的lib文件没有和 主CMakeList.txt放一块的话,就必须要使用绝对路径!然后链接到目标文件中。
MATLAB代码:
function ShowXY(x, y, XRange, YRange)
ax = axes('Box', 'on', 'XLim', XRange, 'YLim', YRange);
plot(x, y, 'Parent', ax)
end
使用 mcc 命令来编译 m代码
mcc -W cpplib:MyLib -T link:lib ShowXY.m
看一下生成的这个MyLib头文件
拖到最后,你会看到一个 CPP API;
可以看到 无输出的 C++ 的接口和原来的M文件的函数接口一摸一样。在它前面还有个 C API,那个是C的接口,这里用不上。
但是,前面还有有两个 C API 是需要用的:
分别是启动这个库的函数,和终结这个库的函数!
C++代码
#include "MyLib.h"
结果:
这里,返回两个变量值
MATLAB代码
function [mean_x, mean_y] = ShowXY(x, y, XRange, YRange)
ax = axes('Box', 'on', 'XLim', XRange, 'YLim', YRange);
plot(x, y, 'Parent', ax)
mean_x = mean(x);
mean_y = mean(y);
end
相同的操作编译成 库,库的名字还和上面一样, 具体的操作略
假设,MATLAB函数有 nargout 个 返回值,那么有返回值的 接口和没返回值得接口区别在于:有返回值的接口的前 nargout+1 个参数都是用来描述MATLAB函数的返回值。紧接着后面的若干个参数才是MATLAB函数的输入。具体看下面的代码实例
C++ 代码
#include "MyLib.h"
结果:
调用MATLAB库生成多个MATLAB的figure
如果你调用MATLAB所生成的库函数生成多个 MATLAB 中的figure,可能会崩溃死。是因为:可能会多次高频地触发MATLAB的figure的 WindowButtonMotionFunction
。这会造成崩溃!
解决办法,网上说的我试了,反正我没成功。后面是采用MATLAB的print
函数保存figure窗口中的图形到图片中,然后显示图片就行,一样的效果!
最好现在MATLAB中测试一下函数有没有问题,再编译!
--- mwArray 的官网链接
https://ww2.mathworks.cn/help/compiler_sdk/cxx/mwarray.html