1 引言
1984年美国的Mathworks公司推出Matlab,到目前为止,它已发展成为国际上最优秀的科技应用软件之一。其强大的科学计算与可视化功能,简单易用的开放式可扩展环境以及多达30多个面向不同领域而扩展的工具箱(Toolbox)支持,使得Matlab在许多学科领域中成为计算机辅助设计与分析、算法研究和应用开发的基本工具和首选平台。但是由于其编译器采用伪编译的方式,在Matlab中编写的程序无法脱离其工作环境而独立运行。针对这个问题,Mathworks公司为Matlab提供了应用程序接口,允许Matlab和其它应用程序进行数据交换,并且提供了C/C++数学和图形函数库,为在其它程序设计语言调用Matlab高效算法提供了可能。
C++语言是新一代的以面向对象(OOP)概念为根本的高级程序设计语言,它的面向对象的概念更加符合程序员开发软件的思维习惯,类封装性和模块化的构造非常适合软件的移植和维护,使用C++开发有助于提高软件工程的质量。VC++是美国微软公司利用C++的底层机理开发的新一代编译器,与其它一些C++编译器相同,都是以C++语言为编译对象。VC++在编译速度和代码优化方面,是同类产品的佼佼者,并且绝大多数的软件开发包都提供了对它的接口,这是VC++得到程序员认可的主要原因。对于理工科研究生和一些偏重于数值计算的工程技术人员,为了能够较好的在短期之内完成一个兼具友善界面和稳定的计算内核的程序,利用软件接口技术,可以借助于第三方的函数库。
2 Matlab应用程序接口
MATLAB提供的应用程序接口按目的一般来说可以分为以下三种:1.利用其它应用程序的优点,例如计算速度快和使用已有算法而制定的接口;2.建立MATLAB与其它应用程序间的数据交换;3.拓广MATLAB的应用范围和应用手段。开发的相应应用程序为MEX文件、MAT文件和MATLAB引擎应用程序。其中MEX文件作为一种动态链接库文件,必须通过在MATLAB的工作环境内调用才能运行;MAT文件用于数据交换,不能利用MATLAB提供的功能来完成计算任务;MATLAB引擎应用程序是一种可以独立执行的应用程序,但在应用程序执行时,将在后台启动一个MATLAB进程,用于接收从应用程序发送来的指令并执行,然后按照要求返回计算结果。综上,基于MATLAB应用程序接口开发的应用程序并不完善,这种应用程序的运行不能脱离MATLAB环境。
3 VC++调用Matlab数学和图形库
6.1版本的Matlab软件包中提供了C/C++的数学和图形库,通过其编译器支持可以将Matlab中编写的m文件转换成以C/C++代码的文件,而且可以将m文件生成dll库,甚至我们可以直接调用其中的库函数,生成并发布不必依赖Matlab的可执行文件。通过mcc编译器生成C/C++代码,进而可以在VC或者其它编译器生成可独立执行的应用程序,具体流程见下图:
图 1
下面具体讨论如何利用Matlab编译器从m文件生成C/C++代码,并嵌入用户代码,生成运行时不需Matlab支持的应用程序。设matsum.m文件,内容如下:
function sum = matbs( a, b )
% sum
sum = a + b
用mcc命令转换为C/C++代码,命令行参数如下
生成C文件
mcc -t -L C matsum.m
生成C++文件
mcc -t -L Cpp matsum.m
生成动态链接库函数
mcc -t -W lib:sum -T link:lib matsum.m
注意①lib:后跟生成的库文件名不能和原m文件名相同(此处为sum),②此处只能生成C语言方式的动态链接库。该编译命令共生成9个文件,其中sum.lib和sum.dll是可以在C/C++应用程序中调用的动态链接库文件。
对生成的C++文件进行分析,不难发现编译器根据m文件中的函数,生成一个对应的C++函数:static mwArray Mmatsum(int nargout_, mwArray a, mwArray b);并分别根据C++和Mex文件的接口方式生成两种形式的接口函数:extern mwArray matsum(mwArray a = mwArray::DIN, mwArray b = mwArray::DIN);和void mlxMatsum(int nlhs, mxArray * plhs[], int nrhs, mxArray * prhs[]);另外还有局部函数表声明1。
下面我们在VC++的IDE环境下进行程序设计,为了论述方便首先生成一个基于C++的简单控制台应用程序,然后在项目中将用mcc命令生成的matsum.cpp和matsum.hpp包含到项目(project)中,如果该项目含有预编译头文件StdAfx.h,那么在matsum.cpp所有包含文件之首加入#include “StdAfx.h”。然后打开Project/Setting…菜单,在C/C++的Category中选Preprocessor,在预定义(Preprocessor definitions)栏加入MSVC,MSWIND,__STDC__,其中__STDC__可选,在C++项目中可省;选Code Generation,在运行时库(Use run-time library)栏选择Multithreaded DLL。在Link的Object/Library modules中添加库文件libmatlb.lib,libmx.lib,libmatpm.lib,当然对于使用图形函数的程序必须使用相应的库。
修改选项参考表一,一般在Code Generation中选择Multithreaded DLL。
使用Static还是DLL工程 |
是否使用多线程库? |
是否使用C调试库
|
使用的C链接库存
|
Static |
否 |
否 |
libc.lib |
Static |
否 |
是 |
libcd.lib |
Static |
是 |
否 |
libcmt.lib |
Static |
是 |
是 |
libcmtd.lib |
DLL |
否 |
否 |
msvcrt.lib(msvcrt.dll) |
DLL |
否 |
是 |
msvcrtd.lib(msvcrtd.dll) |
DLL |
是 |
否 |
msvcrt.lib(mvcrt.dll) |
DLL |
是 |
是 |
msvcrtd.lib(msvcrtd.dll) |
表一
例程如下:
#include "stdafx.h"
// #include "matlab.hpp" //C++方式
#include "matsum.hpp"
#pragma comment (lib, "libmatlb.lib")
#pragma comment (lib, "libmx.lib")
#pragma comment (lib, "libmatpm.lib") // c++ maths library
int main(int argc, char* argv[])
{
mwArray a(100),b(250);
mwArray c;
c=matsum(a,b);
return 0;
}
我们可以用动态链接库的方式调用m文件中的函数,从前面看到的编译命令,共生成包括sum.lib和sum.dll在内的9个文件,拷贝lib和dll文件到项目中。按以下格式调用:sumInitialize(); // 初始化mlfMatsum(); // 对应于Matsum …… sumTerminate(); // 释放 注意:sum.h中的函数的参数和返回值大多为矩阵结构mxArry形式。因为是C语言方式,不详细论述,例程如下:
{
mxArray *ax, *bx, *cx;
sumInitialize();
// ax=mxCreateDoubleMatrix(1,1,mxREAL);
// bx=mxCreateDoubleMatrix(1,1,mxREAL);
mlfAssign(&ax,mlfScalar(200)); // C语言矩阵结构赋值
mlfAssign(&bx,mlfScalar(522));
cx=mlfMatsum(ax,bx);
// mlfPrintMatrix(cx);
sumTerminate();
}
4 VC++环境下的Matlab开发
从上面的转换可以看出,利用Matlab编译器命令生成的C/C++文件,实际上其函数的输入输出参数为mwArray对象。那么可以考虑直接在VC++或其它C++编译器中利用Matlab的库函数来完成简单计算任务。
Matlab C/C++数学函数库中最基本的数据类型为矩阵对象,即mwArray类对象,几乎所有的库函数均以它们作为计算和处理的对象,掌握mwArray对象的操作,对于学习Matlab C/C++数学库的使用是至关重要的。
Matlab C/C++数学库通过类mwArray对数值矩阵、稀疏矩阵、字符矩阵、单元矩阵和结构体矩阵提供了支持,矩阵对象的创建如下:
mwArray A; // 构造一个空矩阵对象
mwArray A(double/int); // 数子的矩阵对象
mwArray A(int row, int col, double/int/unsigned short *real=NULL,
double/int/unsigned short *image=NULL)
mwArray A(const mwArray&) // 拷贝构造矩阵对象
C/C++调用Matlab函数将大量使用矩阵对象的索引操作,完成C/C++数据到mwArray对象的赋值和mwArray对象到C/C++数据的检索。Matlab C/C++函数库提供ExtractScalar和ExtractData两个函数完成索引操作。另外用GetDate()和mxGetPr、mxGetPi组合可以得到指向mwArray对象的数据的指针。
在VC++环境中调用Matlab数学函数库,还要注意异常的处理。Matlab定义一组以mwException为基类的异常类。异常处理如下:
try{
……
}
catch(mwException){
mwDisplayException(ex);
……
}
5 提供简单算例
在计算机图形学中关于曲线、曲面进行插值或拟合,将大量使用求解线性方程组。如在求B样条曲线插值问题中,要先进行反求控制顶点,然后根据B样条基求出进行插值,我们利用VC来完成界面,利用IMSL提供的数学函数完成计算。
例题:已知型值点序列Pi,i=0,1,2…,7:
|
P[0] |
P[1] |
P[2] |
P[3] |
P[4] |
P[5] |
P[6] |
P[7] |
Abscissae x |
8.125 |
8.400 |
9.000 |
9.485 |
9.600 |
9.959 |
10.166 |
10.200 |
Ordinates y |
0.0774 |
0.099 |
0.28 |
0.6 |
0.708 |
1.20 |
1.80 |
2.177 |
表二
求控制顶点序列。
反求B杨条曲线控制顶点的数学问题是:
在VC中调用Matlab C/C++函数库,利用矩阵的左除,可以快速的反求Deboor控制顶点的坐标值,函数如下:
void resDeboorpt(int n, double *ppxs, point *pt, point *deboor)
{
assert(pt!=deboor&&pt!=0&&deboor!=0);
// assign point data
double *ppt=new double[n*2];
for(int i=0;i<n;i++)
{
*(ppt+i)=pt[i].x;
*(ppt+i+n)=pt[i].y;
}
mwArray xzpt(n,2,ppt);
// assign coefficient matrix
mwArray xsjz(n,n,ppxs);
mwArray kzdjz;
try
{
// compute deboor point matrix
kzdjz=mldivide(xsjz,6*xzpt);
}
catch(mwException ex)
{
mwDisplayException(ex);
}
delete []ppa; delete []ppt;
for(i=0;i<n;i++)
{
deboor[i].x=kzdjz.ExtractScalar(i+1);
deboor[i].y=kzdjz.ExtractScalar(i+n+1);
}
}
计算结果 Di, i=0,1,…,9
|
D[0]=D[1] |
D[2] |
D[3] |
D[4] |
D[5] |
D[6] |
P[7] |
D[8]=D[9] |
Abscissae x |
8.08523 |
8.32384 |
9.01941 |
9.59854 |
9.49644 |
10.0157 |
10.1948 |
10.201 |
Ordinates y |
0.07852 |
0.07183 |
0.22819 |
0.69544 |
0.59008 |
1.19226 |
1.84088 |
2.24422 |
表三
Matlab C/C++函数库中的声明方式是extern的,在外部引用库函数时无需另外声明。由于Matlab C/C++库函数采用动态链接库的方式,所以在发布自己的应用程序时必须包括相应的应用程序扩展文件。具体方法如下:运行%MATLAB% /extern/lib/win32/mglinstaller.exe,所生成的*.dll 包括bin and toolbox打包即可。注意在运行机上设置路径。
6 小结
本文针对 Matlab 应用程序接口,详细论述了在可视化编程环境中,利用 VC++ 调用 Matlab 数学图形函数库,编制可以独立运行的应用程序。经过实践证明,可以在很大程度上提高软件开发效率,增加算法的稳定程度。目前已有一些利用混合编程开发的软件系统:如雷达特性分析、实时预测软件等,在实际中已得到了应用。