转自:http://anony3721.blog.163.com/blog/static/51197420111145534021
缘起:看了网上介绍Mex的文章,玩玩不给初学的新手将很基础的概念。而且将了半天不把自己的例子贴出了,或者贴的例子不能运行,这给入门造成了障碍。我想学,看了一下午都没有弄明白,晚上花了一晚上看了Matlab的官方帮助,受益匪浅,终于懂了,贴出来给大家指条明路,希望后人少走些弯路,减少不必要的摸索时间。
Workflow of a MEX-File
This section discusses MATLAB API functions for handling the basic workflow of a MEX-file and uses C language code snippets for illustration. For an example of a complete C program, see Creating a Source MEX-File. Unless otherwise specified, in this section the term "MEX-file" refers to a source file.
Some basic programming tasks are:
1 Creating a Gateway Function
2 Declaring Data Structures
3 Managing Input and Output Parameters
4 Validating Inputs
5 Allocating and Freeing Memory
6 Manipulating Data
7 Displaying Messages to the User
8 Handling Errors
9 Cleaning Up and Exiting
1)MEX文件是由原C代码加上MEX文件专用的接口函数后编译而成的,本质是一个加了MATLAB调用规范的DLL文件。
2)DLL函数本质上是实现h头文件中的接口声明的。所以可以这样理解,MEX文件实现了一种接口,它把在Matlab中调用函数时输入的自变量通过特定的接口调入了C函数,得出的结果再通过该接口调回Matlab。该特定接口的操作,包含在mexFunction这个函数中,由使用者具体设定。
1. MEX的编写格式
a. MEX之Hello world程序
1)将以下的代码保存成 hello.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{ mexPrintf("Hello, world!\n"); }
2) mex hello.c
3) 在Matlab 提示符下输入 hello 看看结果
四个参数分别用来输出和输入数据:
注意: 我们对输出和输入参数的操作都是通过指针的方式进行的。(这点很容易理解,因为我们的计算结果是需要传递给MATLAB的,实际上我们传递的不是数据,而是指针。MATLAB可以通过这些指针,访问内存中的数据。)
b. 操作输入数据
对输入数据进行操作,需要通过MEX函数mxGetPr 得到数据的指针地址。 mxGetM 和 mxGetN 得到矩阵数据的行和列 (返回整数)。对于实矩阵,我们可以定义 double *M; 来对实矩阵数据操作。如:
double *M;
int m,n;
// 指针指向第一个参数的数据地址
M = mxGetPr(prhs[0]);
m = mxGetM(prhs[0]);
n = mxGetN(prhs[0]);
需要注意的是,MATLAB矩阵数据的存储顺序是"从上到下,从左到右"的,这点和Fortran是一样的。也就是说对于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开始。
使用mex时的注意事项:
1.MATLAB调用mex接口时,将参数个数及参数指针传入接口子程序,由接口子程序完成指针和调用变量的赋值、输出数据的内存空间分配,接口子程序再将参数指针或经过赋值的变量作为参数传递给C的计算子程序,完成调用过程。调用时应注意指针所指对象的正确性,为处理正确最好做相应的强制类型转换。
2.mex并不便于调试,因此应在C的IDE中用测试集调试后再放入mex文件中。
3.MATLAB中指向二维及高维数组的指针递增方式是按行递增的,而C中是按列递增的,因此计算index时要注意位置。
c. 操作输出数据
对于输出数据,我们需要首先分配内存空间,有专门的mex函数可以使用,如:
plhs[0] = mxCreateDoubleMatrix(m,n, mxREAL); //生成m x n 的实矩阵。
同输入数据一样,要对输出数据操作,我们也需要一个指向数据的指针变量,如
double *A;
A = mxGetPr(plhs[0]);
下面介绍一下如何使用VS2008编写MEX并编译调试。
2. VC中编写MEX
打开VS2008, 新建项目, 选择MFC DLL.
a. 配置项目属性
打开项目属性配置页,C++ -> 附加包含目录 加入MATLAB安装目录下的 \extern\include 路径。
连接器 -> 附加库目录 加入MATLAB的 \extern\lib\win32\microsoft 路径。
连接器 -> 输入 -> 附加依赖项 输入libmx.lib libeng.lib libmat.lib libmex.lib 这四个lib文件。
b. 编辑输出函数
在项目源文件的. def 中EXPORTS段加入 mexFunction, 如:
EXPORTS
c. 编写MEX文件
项目文件中新建一个C++文件 如 mexproc.cpp,里面按前面介绍的格式编写代码即可。
d. VC编译MEX
像编译其他程序那样直接编译即可,成功会生成dll文件。如果编译链接时出错,根据错误提示,检查一下lib和h的路径是否正确,有无缺少lib文件,代码是否有语法错误等。
3. VC中调试MEX
要调试MEX程序就要先编译,再调用她。所以我们需要在MATLAB中调用这个函数,并在VC的MEX程序相应位置处下断点即可。调用的函数名就是dll的主文件名,你可以根据自己的需要改名。我们用mymexfun.dll为例,先在VC的 mexFunction 函数代码段开始处F9下断。然后Ctrl+Alt+P附加MATLAB.exe进程。这样就可以运行命令调试程序了。我们可以在MATLAB的命令行里输入命令:
(如果命令找不到,检查一下matlab当前路径,和path路径。)
程序一旦被调用,就会被断在我们的断点处。接着你就可以像调试C++程序那样调试MEX程序了。
在MATLAB中编译MEX可以输入:
MATLAB上编译MEX时,你可以选择不同的编译器如lc, gcc等。也可以在编译时附加lib和h文件。关于mex的命令详解请参考MATLAB帮助文档。
======================================================================================================
mex的适用情况:①需要大量for循环 ②不想改写已有的C模块,这些情况下适用mex接口比较合适。
//////////////////////////////////////////////
1. 编辑mex源代码 arrayProduct.c, 相当于DLL源文件
/*==========================================================
* arrayProduct.c - example in MATLAB External Interfaces
*
* Multiplies an input scalar (multiplier)
* times a 1xN matrix (inMatrix)
* and outputs a 1xN matrix (outMatrix)
*
* The calling syntax is:
*
* outMatrix = arrayProduct(multiplier, inMatrix)
*
*========================================================*/
#include "mex.h"
/* The computational routine */ ①Your MEX Source File 是真正的算法实现的地方
// Computational Routine 是实际执行真正执行运算的部分,就是C语言中DLL的函数
void arrayProduct(double x, double *y, double *z, mwSize n)
{
/* The MX Matrix Library and MEX Library functions use MATLAB preprocessor macros for cross-platform flexibility.
Edit your computational routine to use mwSize for mxArray size n and index i. */
mwSize i; // Use Preprocessor Macros
/* multiply each element y by x */
for (i=0; i<n; i++) {
z[i] = x * y[i];
}
}
/* The gateway function */
/ *After the computational routine, add the gateway routine mexFunction
然后我们来建立其与MATLAB的间接口。MEX函数库里有个mexFunction()函数,它相当于C语言中的main()函数。所以这里我们知道了MEX源文件没有main(),而是以一个mexFunction()代替。 */
// 这个GateWay函数中仅仅规范了C程序的输入输出和MATLAB程序的输入输出,
int nrhs, const mxArray *prhs[])
/* 其中nlhs (number of left-hand side) 是输出参数的个数,nrhs (number of right-hand side) 是输入参数的个数。
例如对于c=add(a,b),有 nlhs=1,hrhs=2。
plhs[]与prhs[]都是指针数组,也就是说它是个数组,每个元素都是一个指针,这些指针指向的东东的类型是mxArray。那什么是mxArray呢?可以把他理解MATLAB中的矩阵,因为MATLAB中所有数据都是以矩阵的形式保存的。
注意不能用*(prhs[0])得到a的值。因为prhs[0]指向的东西的类型是mxArray(参数列表里看出),想把它的值以我们常用的数值形式赋给一个标量(Scalar),得使用mxGetScalar()函数转化一下,对于矩阵要用mxGetPr()获取其指针*/
{
double multiplier; /* input scalar */
double *inMatrix; /* 1xN input matrix */
size_t ncols; /* size of matrix */
double *outMatrix; /* output matrix */
/* check for proper number of arguments */
if(nrhs!=2) {
mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs","Two inputs required.");
}
if(nlhs!=1) {
mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nlhs","One output required.");
}
/* make sure the first input argument is scalar */
if( !mxIsDouble(prhs[0]) ||
mxIsComplex(prhs[0]) ||
mxGetNumberOfElements(prhs[0])!=1 ) {
mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notScalar","Input multiplier must be a scalar.");
}
/* check that number of rows in second input argument is 1 */
if(mxGetM(prhs[1])!=1) { // The second input argument must be a row vector.
mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector","Input must be a row vector.");
}
/* get the value of the scalar input */
/* 注意不能用*(prhs[0])得到a的值。因为prhs[0]指向的东西的类型是mxArray(参数列表里看出),想把它的值以我们常用的数值形式赋给一个标量(Scalar),得使用mxGetScalar()函数转化一下 */
multiplier = mxGetScalar(prhs[0]);
/* create a pointer to the real data in the input matrix */
inMatrix = mxGetPr(prhs[1]); // create a pointer to the real data in the input matrix
/* get dimensions of the input matrix */
ncols = mxGetN(prhs[1]);
/* create the output matrix */
/* plhs对应的输出的内容。plhs[0]这个指针指向输出的第一个参数,就是c=add(a, b)中的c了。记住这个c应该是以mxArray的类型出现的,为了得到mxArray类型的输出量,要使用mxCreateDoubleMatrix()函数,它创建一个指向mxArray类型的指针。 */
plhs[0] = mxCreateDoubleMatrix(1,(mwSize)ncols,mxREAL);
// 参数(1, 1, mxREAL)定义了对应c的尺寸类型,MATLAB中c是以1×1的实数矩阵形式报保存的。
/* get a pointer to the real data in the output matrix */
// 而使用mxGetPr()函数可以得到plhs[0]指向的mxArray的第一个double类型的指针。
outMatrix = mxGetPr(plhs[0]);
/* call the computational routine */
/* 最后调用下C语言的函数,将C函数计算的结果给MATLAB翻译一下。回顾DLL中的函数被exe程序调用时,不用这个翻译过程,直接从odinal number找到该函数的入口地址执行就行了,mex和dll二者的实质内容是一样的,仅仅是输入输出有些差异。所以称Mex文件为一个变异的DLL是有道理的*/
arrayProduct(multiplier,inMatrix,outMatrix,(mwSize)ncols);
}
2. Build the Binary MEX-File, 相当于编译后的DLL文件
mex arrayProduct.c
3. 测试Binary Mex File,相当于EXE程序调用DLL
Type:
s = 5; A = [1.5, 2, 9]; B = arrayProduct(s,A)MATLAB displays:
B = 7.5000 10.0000 45.0000To test error conditions, type:
arrayProductMATLAB displays:
Error using arrayProduct Two inputs required.