Matlab 编译C/C++源文件并调用
Posted 大作家佚名
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Matlab 编译C/C++源文件并调用相关的知识,希望对你有一定的参考价值。
简介
C++ MEX 函数,MEX(即 MEX 可执行程序)指自动加载的、可以像任何 MATLAB函数一样调用的程序。使用 MATLAB mex 命令编译您的 C++ MEX 程序:mex MyMEXCode.cpp
MEX文件的调用极为方便,其调用方式与MATALAB的内建函数完全相同,只需要在命令窗口内输入对应的文件名称即可。通过MEX函数,可以将复杂、底层的功能由C/C++实现,然后由Matlab调用。
编译器环境
输入命令mex -setup
如果出现下面提示,需要安装编译器,安装步骤如下:
步骤1
步骤2
下载安装程序后双击打开。
步骤3
测试
安装后,重新输入命令mex -setup
显示如下,表示安装成功
>> mex -setup
MEX 配置为使用 'MinGW64 Compiler (C)' 以进行 C 语言编译。
要选择不同的语言,请从以下选项中选择一种命令:
mex -setup C++
mex -setup FORTRAN
如果编译的时候出现错误如下,则将C源文件夹加入路径即可解决
> In demo_mex (line 25)
错误使用 mex
C:/ProgramData/MATLAB/SupportPackages/R2020a/3P.instrset/mingw_w64.instrset/bin/../lib/gcc/x86_64-w64-mingw32/6.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
cannot open output file csf_filtering.mexw64: Permission denied
MATLAB编译C/C++代码
mexFunction函数
MEX文件的源代码一般由两部分组成:
(1) 计算过程。该过程包含了MEX文件实现计算功能的代码,是标准的C语言子程序。
(2) 入口过程。该过程提供计算过程与MATLAB之间的接口,以入口函数mxFunction实现。在该过程中,通常所做的工作是检测输入、输出参数个数和类型的正确性,然后利用mx-函数得到MATLAB传递过来的变量(比如矩阵的维数、向量的地址等),传递给计算过程。
计算子例程由C/C++实现比较简单,这里先介绍接口子程序。MEX程序开始函数是mexFunction
函数,与C/C++开始函数为main
一个道理为入口函数。MEX文件的计算过程和入口过程也可以合并在一起。但不管那种情况,都要包含#include “mex.h”,以保证入口点和接口过程的正确声明。注意,入口过程的名称必须是mexFunction,并且包含四个参数,即:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
nlhs:输出参数数目 (Number Left-hand side),等号左边
plhs:指向输出参数的指针 (Point Left-hand side),等号左边
nrhs:输入参数数目 (Number Right-hand side),等号右边
prhs:指向输入参数的指针 (Point Right-hand side),等号右边。要注意prhs是const的指针数组,即不能改变其指向内容。
注意: 我们对输出和输入参数的操作都是通过指针的方式进行的。因为我们的计算结果是需要传递给MATLAB的,实际上我们传递的不是数据,而是指针(地址)。MATLAB可以通过这些指针,访问内存中的数据。
prhs[i]和plhs[i]都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型。
MATLAB在调用MEX文件时,输入和输出参数保存在两个mxArray*类型的指针数组中,分别为prhs[]和plhs[]。在函数内部调用时,常用函数用法如下:
mxlsNumeric(prhs[0]); //判断变量是否是数字
mxlsDouble(prhs[0); //判断变量是否为双精度
mxlsEmpty(prhs[0); //判断变量是否为空值
mxIsComplex(prhs[0]); //判断变量是否为复数
mexPrintf("hello,world!\\n"); //命令行中打印数据
mxGetM(prhs[0]); //获取输入参数的行
mxGetN(prhs[0]); //获取输入参数的列
points = mxGetScalar(prhs[1]); //将返回变量的实部,为双精度形数据
plhs[0]= mxCreateDoubleMatrix(1, 1, mxREAL);//创建1x1双精度返回变量
X = mxGetData(prhs[0]); //获取变量的指针,功能相当于mxGetPr()
y = mxGetPr(plhs[0]); //获取变量的指针,数据为实数
对mexFunction的参数是进行指针操作的,不能用单纯的return返回值。
输入数据
对输入数据进行操作,需要通过MEX函数mxGetPr 得到数据的指针地址。 mxGetM 和 mxGetN 得到矩阵数据的行和列 (返回整数)。对于实矩阵,我们可以定义 double *M
; 来对实矩阵数据操作(不过似乎是,plhs, prhs都是指向double类型的指针,所以下面的这个M等,都要定义成double*类型的)。如:
double *M;
int m, n;
M = mxGetPr(prhs[0]); // 指针指向第一个参数的数据地址
m = mxGetM(prhs[0]);
n = mxGetN(prhs[0]);
需要注意的是,MATLAB矩阵数据的存储顺序是"从上到下,从左到右"。也就是说对于MATLAB的m x n的矩阵A。 A(1,1) 就是 *M,A(2,1) 就是 *(M+1) ,以此类推,A(i, j) 就是 (M + m(j-1) + (i-1))。注意: MATLAB的指标从1开始,C的指标从0开始。
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
int i;
i = mxGetScalar(prhs[0]);
在这个程序里用到了一个函数:mxGetScalar,"Scalar"就是标量的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整形变量i。既然有标量,显然还应该有矢量,否则矩阵就没法传了。看下面的程序:
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
double *i;
i = mxGetPr(prhs[0]);
这样,就通过mxGetPr函数从指向mxArray类型数据的prhs[0]获得了指向double类型的指针。
但是,还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢 ?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就没法对它进行计算。
为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来:
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
double *data;
int M, N;
int i, j;
data = mxGetPr(prhs[0]); //获得指向矩阵的指针
M = mxGetM(prhs[0]); //获得矩阵的行数
N = mxGetN(prhs[0]); //获得矩阵的列数
for (i = 0; i < M; i++)
for (j = 0; j < N; j++)
mexPrintf("%4.3f ", data[j*M + i]);
mexPrintf("\\n");
在里用到了屏幕输出函数mexPrintf(用法跟c里的printf函数几乎完全一样)。
需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为零,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组大data后对应于data[j*M+i] 。
输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请。
输出数据操作
对于输出数据,我们需要首先分配内存空间,有专门的mex函数可以使用,如:
plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL); //生成m x n 的实矩阵。
同输入数据一样,要对输出数据操作,我们也需要一个指向数据的指针变量,如
double *A;
A = mxGetPr( plhs[0]);
为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用 mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
double *inData;
double *outData;
int M, N;
int i, j;
inData = mxGetPr(prhs[0]);
M = mxGetM(prhs[0]);
N = mxGetN(prhs[0]);
plhs[0] = mxCreateDoubleMatrix(M, N, mxREAL);
outData = mxGetPr(plhs[0]);
for (i = 0; i < M; i++)
for (j = 0; j < N; j++)
outData[j*M + i] = inData[(N - 1 - j)*M + i];
当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。
通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的re由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差,以下程序则容错性较好
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
double *inData;
double *outData;
int M, N;
//异常处理
//异常处理
if (nrhs != 1)
mexErrMsgTxt("USAGE: b=reverse(a)\\n");
if (!mxIsDouble(prhs[0]))
mexErrMsgTxt("the Input Matrix must be double!\\n");
inData = mxGetPr(prhs[0]);
M = mxGetM(prhs[0]);
N = mxGetN(prhs[0]);
plhs[0] = mxCreateDoubleMatrix(M, N, mxREAL);
outData = mxGetPr(plhs[0]);
for (i = 0; i < M; i++)
for (j = 0; j < N; j++)
outData[j*M + i] = inData[(N - 1 - j)*M + i];
在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出错提示的同时退出当前程序的运行。MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mex前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mexErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。至此为止,使用C编写mex函数的基本过程已经介绍完了。
实例测试
完整的test_add.cpp如下:
#include "mex.h" //使用MEX文件必须包含的头文件
//执行具体工作的C函数
double add(double x, double y)
return x + y;
// MEX文件接口函数
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
double *a;
double b, c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
a = mxGetPr(plhs[0]);
b = *(mxGetPr(prhs[0]));
c = *(mxGetPr(prhs[1]));
*a = add(b, c);
mexFunction
的四个参数皆是说明Matlab调用MEX文件时的具体信息,Matlab如这样调用函数时:
b = 1.1; c = 2.2;
a = test_add(b, c)
因为现在左面只有一个变量,即该数组只有一个指针,plhs[0]指向的结果会赋值给a。prhs和plhs类似,因为右手面有两个自变量,即该数组有两个指针,prhs[0]指向了b,prhs[1]指向了c。
因为Matlab最基本的单元为array,无论是什么类型也好,如有double array、 cell array、 struct array……所以a,b,c都是array,b = 1.1便是一个1x1的double array。而在C语言中,Matlab的array使用mxArray类型来表示。所以就不难明白为什么plhs和prhs都是指向mxArray类型的指针数组。
mexFunction的内容是什么意思呢?我们知道,如果这样调用函数时:
output = add(1.1, 2.2);
在未涉及具体的计算时,output的值是未知的,是未赋值的。所以在具体的程序中,我们建立一个1x1的实double矩阵(使用 mxCreateDoubleMatrix函数,其返回指向刚建立的mxArray的指针),然后令plhs[0]指向它。接着令指针a指向plhs [0]所指向的mxArray的第一个元素(使用mxGetPr函数,返回指向mxArray的首元素的指针)。同样地,我们把prhs[0]和prhs [1]所指向的元素(即1.1和2.2)取出来赋给b和c。于是我们可以把b和c作自变量传给函数add,得出给果赋给指针a所指向的mxArray中的元素。因为a是指向plhs[0]所指向的mxArray的元素,所以最后作输出时,plhs[0]所指向的mxArray赋值给output,则 output便是已计算好的结果了。
实际上mexFunction是没有这么简单的,我们要对用户的输入自变量的个数和类型进行测试,以确保输入正确。如在add函数的例子中,用户输入char array便是一种错误了。
从上面的讲述中我们总结出,MEX文件实现了一种接口,把C语言中的计算结果适当地返回给Matlab罢了。当我们已经有用C编写的大型程序时,大可不必在 Matlab里重写,只写个接口,做成MEX文件就成了。另外,在Matlab程序中的部份计算瓶颈(如循环),可通过MEX文件用C语言实现,以提高计算速度。
一个简单的MEX文件例子:用m文件建立一个1000×1000的Hilbert矩阵。
在matlab中新建一个Matlab_1.cpp 文件并输入以下程序:
#include "mex.h"
void hilb(double *y, int n)
int i, j;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
*(y + j + i*n) = 1 / ((double)i + (double)j + 1);
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
double x, *y;
int n;
if (nrhs != 1)
mexErrMsgTxt("One inputs required.");
if (nlhs != 1)
mexErrMsgTxt("One output required.");
if (!mxIsDouble(prhs[0]) || mxGetN(prhs[0])*mxGetM(prhs[0]) != 1)
mexErrMsgTxt("Input must be scalars.");
x = mxGetScalar(prhs[0]);
plhs[0] = mxCreateDoubleMatrix(x, x, mxREAL);
n = mxGetM(plhs[0]);
y = mxGetPr(plhs[0]);
hilb(y, n);
该程序是一个C语言程序,它也实现了建立Hilbert矩阵的功能。在MATLAB命令窗口输入以下命令:mex Matlab_1.cpp,即可编译成功。进入该文件夹,会发现多了一个文件:Matlab_1.mexw32,其中Matlab_1.mexw32即是MEX文件。运行下面程序:
Matlab代码测试:
tic
m=10000;
n=10000;
a=zeros(m,n);
for i=1:10000
for j=1:10000
a(i,j)=1/(i+j);
end
end
toc
tic
a=Matlab_1(10000);
toc
输出
历时 1.449749 秒。
历时 0.581196 秒。
由上面实验看出,同样功能的MEX文件比m文件快得多。
字符串类型传入、传出
matlab -> c++ (传入)
char *input_buf;
input_buf = mxArrayToString(prhs[0]); //使用mxArrayToString将mxArray转换为c、c++字符串
c++ -> matlab (传出)
char *output_buf; //定义字符串缓存
size_t buflen = (mxGetM(prhs[0]) * mxGetN(prhs[0])) + 1; //获取字符串长度,mxGetM获取行数,mxGetN获取列数
output_buf=mxCalloc(buflen, sizeof ( char )); //使用mxCalloc分配输出字符串数组
plhs[0] = mxCreateString(output_buf); //使用mxCreateString创建mxArray输出
mxfree(output_buf);
参考
https://blog.csdn.net/zacharyzqc/article/details/83866888
https://blog.csdn.net/yshshhgxq/article/details/91351535
https://blog.csdn.net/weixin_30354675/article/details/96341122
https://www.cnblogs.com/huty/p/8518790.html
https://blog.csdn.net/yimiyangguang185/article/details/52623075
以上是关于Matlab 编译C/C++源文件并调用的主要内容,如果未能解决你的问题,请参考以下文章