Mex文件怎么用?

时间:2020-12-25 06:47:14

转自: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 看看结果

四个参数分别用来输出和输入数据: nlhs 输出参数个数,plhs 输出参数指针 ,nrhs和prhs是输入参数相关的。

注意: 我们对输出和输入参数的操作都是通过指针的方式进行的。(这点很容易理解,因为我们的计算结果是需要传递给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
    ; 此处可以是显式导出
    mexFunction

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的命令行里输入命令:

          [输出变量] = mymexfun(输入变量)

(如果命令找不到,检查一下matlab当前路径,和path路径。)

程序一旦被调用,就会被断在我们的断点处。接着你就可以像调试C++程序那样调试MEX程序了。

在MATLAB中编译MEX可以输入: mex 文件名.cpp

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程序的输入输出,

void mexFunction( int nlhs, mxArray *plhs[],
                  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.0000   

To test error conditions, type:

arrayProduct

MATLAB displays:

Error using arrayProduct  Two inputs required.