我们热爱生命科学!-生物行

[推荐] MATLAB 与 C 语言的接口

时间:2006-10-28 23:13来源:Internet 作者:admin 点击: 4068次
MATLAB 与 C 语言的接口


东北大学 薛定宇 (原作) 


    MATLAB 到 C 语言程序的转换可以由两种途径完成,其一是 MATLAB 自己提供的 C 语言翻译程序 mcc, 另一种是原第 3 方公司 MathTools 开发的 MATCOM。后者出现较早,功能远比 MATLAB 自己的翻译程序强大,所以 MathTools 公司已经被 MathWorks 公司收购,并已将其开发技术融于新推出的 MATLAB 6.0 (Release 12) 中。

   依本人的观点,二者都不完善,有大量的 MATLAB 函数及数据类型是不能兼容变换的,所以有时会发现将简洁的 MATLAB 程序翻译成 C 语言程序很晦涩。翻译后的 C 程序几乎不适合手动去维护,更谈不上升级了。

   有几个不兼容的地方是不能令人容忍的,例如 eval() 函数在 C 翻译下是不兼容的,最新的 MATLAB 6 才刚刚敢说已经部分兼容了,但离真正的兼容还有很长的路要走,有很多艰难的技术困难需要克服,短时间内不可能有突破性的进展。另一个大的问题是,众所周知,MATLAB 新的控制系统工具箱中定义了大量的 LTI 类,MATLAB 5 开始也允许使用类与对象的概念,这已经向“面向对象”的编程走了一大步,但 MATLAB 到 C 的翻译明显都不支持“对象”的数据类型,所以在控制系统分析与设计 (当然还有其他的领域) 中这样的转换几乎没有什么实际意义,所以不要将这样的转换太理想化。

   从转换的程序速度来看,因为 mcc 和 MATCOM 都沿用了 MATLAB 的程序运算机制,所以不可能大幅度地提高程序运行速度。相反地,如果将 MATLAB 程序中明显的瓶颈部分用 C 语言按照 MEX 格式编写,则可以大大加快速度。在《科学运算语言 MATLAB 5.3 程序设计及应用》一书中以例子做过探讨,MCC 可以比快 50%, 而 MEX 的形式可能快上百倍。

〖举例〗分形问题:从初始点(x0,y0) 出发,按下面的公式生成新的点 (x1,y1)

其中 gi 为 (0,1) 区间均匀分布的随机数。要求从 (x1,y1) 再生成 (x2,y2),...,直至生成 1,000,000 个点(有点夸张,但太少了用 MATLAB 测时间不准)。可以下载用 MATLAB 的 M-函数写的程序、用 Mex_C 写的程序。程序调用语句为:
   >> N=1000000; v=rand(N,1); tic; [x,y]=frac_tree(0,0,v,N); toc
  然后测试一下速度,再想法用 MATCOM 或 MCC 转换一下,再试试速度。问题:MATCOM 程序到底比 M 函数快多少?我用 Mex_C 实现的速度可是快100多倍啊!下面有文章介绍,用 MATCOM+VC 能平均快1.5倍 (在别的应用上),依的观点,为了这区区 1.5 倍还不至于自己给自己找麻烦去用 MATCOM。

   这样看,可能 MATLAB 到 C 转换唯一的好处就是它能开发具有自主版权的软件了。 所以本人一般不太赞同用这样的转换方法去开发程序,尤其不赞成刻意追求 MATLAB + C 的方式。本人认为:除非必要,一般别采用这样的方法。提出这样的观点,欢迎商榷。

   MATCOM 目前有 BC 和 VC 两个程序,MATLAB + VC 之家中收录了一些文章。见到有下载区有这样的软件。

在这里给出一个下载区的链接
C++Builder 调用 Matlab 的实现方法


国防科技大学机电工程与仪器系 张云洲

摘要:本文简要叙述了Matlab语言的优点,介绍了在用Borland C++Builder语言开发的Windows应用程序中,调用matlab语言实现科学计算和图形绘制的方法。

关键词:C++Builder Matlab调用 混合编程

1. 引言

  Matlab是一个高度集成的系统,集科学计算、图象处理、声音处理于一体,具有极高的编程效率。近年来,Matlab已经从最初的“矩阵实验室”,渗透到科学与工程计算的多个领域,在自动控制、信号处理、神经网络、模糊逻辑、小波分析等多个方向,都有着广泛的应用。

  Borland C++Builder是一种新颖的可视化编程语言。在工程应用中,我们一般用C++Builder语言编写应用程序,实现交互界面、数据采集和端口操作等,但C++Builder在数值处理分析和算法工具等方面,其效率远远低于Matlab语言。在准确方便地绘制数据图形方面,Matlab语言更具有无可比拟的优势。此外,Matlab还提供功能强大的工具箱。但Matlab的缺点是不能实现端口操作和实时控制。因此,若能将两者结合运用,实现优势互补,将获得极大的效益。

  本文结合实际介绍了应用Borland C++Builder3.0开发的Windos应用程序中,对Matlab的调用方法。

2. C++Builder调用Matlab的实现方案

2.1 实现思路

  在高版本的Maltab中(如Matlab V4.2)提供了DDE接口,用户可以通过Windows的DDE通讯基制实现外部调用。这种实现方式比较简单,但将增大主程序代码,影响运行速度。

  在Windows系统中,DLL是一种很特别的可执行文件,可以被多个Windows应用程序同时访问,具有固定的共享数据段。该数据段的数据在DLL被Windows下载前会一直保留在内存中,因此可以通过DLL实现用户程序与Matlab之间的数据传输和函数调用。

  具体地说,就是利用Matlab的32位动态连接库(DLL),生成相应的可以被C++Builder调用的DLL,用来提供二者之间的基本支撑环境。只需在用户程序中加载该DLL,即可实现其数据段的共享。然后在用户程序中操作DLL数据段的数据,并通过某种方式在用户程序中使Matlab执行该DLL,就可实现用户程序对Matlab的调用。其形式可以是混合编程或函数调用,非常方便而高效。

2.2 实现方式

  Matlab提供了可外部连接的DLL文件,通过将其转换为相应的Lib文件,并加以必要的设置,就可以在C++Builder中直接进行Matlab函数调用,实现C++ Builder语言与Matlab语言的混合编程。

2.2.1 运行环境要求

  由于Matlab提供的是32位的DLL。其运行环境要求是Matlab V4.2或更高版本。C++Builder可以进行32位编程,这里我们采用的是V3.0版本。

2.2.2 C++Builder下LIB文件的生成

  Matlab提供的Def文件允许用户通过Implib命令生成相应的Lib文件。其命令格式为:

  Implib ???.lib ???.def

  在< matlab >\extern\include目录下,提供了如下三个.Def文件:

  _libeng.def,_libmat.def,_libmx.def

  通过上述命令可以生成相应的三个Lib文件。这些Lib文件中包含了可外部调用的Matlab函数的必要信息。

3. C++Builder调用Matlab实现计算和绘图

  为清楚起见,通过一个简单的CBuilder例程进行说明。该实例通过调用Matlab实现矩阵运算并绘制图形,来演示C++Builder对Matlab的调用。在C++Builder编辑环境中,建立一个新的窗体MyForm,并放置一个按钮Demo。将工程文件命名为Try.prj,其主函数为try.cpp。在主函数中,我们将使用一个实现Matlab调用的子函数DemoMatlab,作为按钮Demo的响应事件。其源代码如下:

#include < vcl.h >
#pragma hdrstop

#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TMyForm *MyForm;

__fastcall TMyForm::TMyForm(TComponent* Owner)
: TForm(Owner)
{
}

void __fastcall TMyForm::DemoClick(TObject *Sender)
{
DemoMatlab(); //演示Matlab语言调用
}
---- 为了调用Matlab中的函数,必须进行必要的设置,将包含这些函数的文件加入工程文件Try.prj。以下是操作过程:

  a. 在头文件中加入Engine.h。其包含了启动Matlab调用和关闭的函数声明。

  b. 打开Project|Option…对话框,点击Directories/Conditionals

在Include Path中,加入目录路径< matlab >\extern\include,该路径包含了engine.h和matlab.h等有用的头文件。

在Library Path中,加入< matlab >\bin和< matlab >\extern\include。这两个目录路径包含了可外部调用的DLL和LIB文件。
  c. 点选Project|Add to Project…对话框,加入如下库文件:

  _libeng.lib,_libmat.lib和_libmx.lib。

  在进行了这些必要的设置之后,我们就可以选用适当的函数来实现目标。 以下是子函数DemoMatlab的程序代码。

    void DemoMatlab
{
Engine *eng; //定义Matlab引擎
char buffer[200]; //定义数据缓冲区
int array[6]={1,2,3,4,5,6};
mxArray *S = NULL, *T = NULL;
engOpen(NULL); //打开MATLAB 引擎 ---①

    S= mxCreateDoubleMatrix(1,6, mxREAL);
// 产生矩阵变量
mxSetName(S, "S");
    memcpy((char *) mxGetPr(S),
(char *) array, 6*sizeof(int));
    engPutArray(eng, S); //将变量X置入Matlab的工作空间
engEvalString(eng, "T = S/S.^2;");
//计算
engEvalString(eng, "plot(S, T);");
//绘制图形
…… ……
engOutputBuffer(eng, buffer, 200);
//获取Matlab输出
    T = engGetArray(eng, "T");
//获得计算结果----②

    engClose(eng);
//关闭Matlab引擎,结束调用
    mxDestroyArray(S);
//释放变量
    mxDestroyArray(T);
    }
  若还需要执行其它功能和任务,那么按照上面介绍的方法,进行变量声明后,在①、②处加写需要的语句既可。

  当然,使用这种方法调用Matlab不能脱离Matlab环境的支撑。但当我们不需要看到Matlab的命令窗口时,可将其赋予Swhide属性而加以隐藏。

4. 结语

  按照本文介绍的方法来实现C++Builder下应用程序对Matlab的调用,可以充分利用Matlab强大的科学计算功能和丰富的工具箱,而且具有混合编程、方便高效的优点。这是C++语言和其它高级语言所无法比拟的。按照本文的方法,我们还可以编写程序来最充分地利用Matlab的其它资源,开发满足自己需要的程序,更有效地完成我们的工作。

VC++ 下如何利用 Matlab 工具箱进行数字信号处理


潘 卫 明 赵 敏 张 进 芳
(南京航空航天大学 测试工程系 210016)

摘要:本文详述了在Vc环诚氯绾卫?肕atlab工具箱进行数字信号处理,全文以Matlab工具箱中功率谱密度分析函数为例,介绍了通过Matlab自带的引擎、Matlab自身的编译器以及利用MathTools公司的Matcom进行对工具箱函数的调用。

关键词:Matlab M-文件 引擎 编译器 Matcom Vc++

  Matlab的信号处理工具箱是信号算法文件的集合,它处理的基本对象是信号与系统,信号处理工具箱位于目录、Toolbox\Signal下,利用工具箱中的文件可以实现信号的变换、滤波、谱估计、滤波器设计等。在其它的环境如Vc下如果能调用Matlab工具箱中的文件,会大大地加快一些算法的实现,同时其可靠性也很高。

利用Matlab引擎
  Matlab引擎采用客户和服务器计算方式,在运用中,Vc的C语言或C++语言的程序作为前端客户机,它向Matlab引擎传递命令和数据信息,并从Matlab引擎接收数据信息,它提供了下列几个函数: engOpen, engGetArray, engPutArray, engEvaString,

engOutputBuffer ,engClose与客户机进行交互。

  下面例程是在Vc下建一个基于对话框的应用程序,在对话框中设置一个Button控件OnMatlabEngine.,在对话框 .cpp文件中加入”engine.h” 和“math.h” 头文件,下面给出部分程序清单。

Void CtestmatlabDlg::OnMatlabEngine(){
Engine *ep;
mxArray* T=NULL,*result=NULL,*mFs=NULL,*mnfft= NULL;
double datax[1024];
char buffer[1024];
for(int j=0;j<1024;j++)//注:如通过采集卡采集数据可将采集的数据放在datax[]数组中,此循环就不需要
{
double samt=(double)(1.0/1024);
datax[j]=sin(2.0*63.0*samt*3.1415926+1.15*3.1415926);
}
double *pPxx,*pFxx;
if(!(ep=engOpen(" \0"))){//打开Matlab引擎,建立与本地Matlab的连接
fprintf(stderr,"\n Can't start MATLAB engine\n");
exit(-1);
}
double Fs[1]={1024};//因为Matlab所有参与运算的参数都是矩阵的形式,因而下列几行将参数转变
double nfft[1]={1024};//成Matlab可接受的矩阵形式。
T=mxCreateDoubleMatrix(1,1024,mxREAL);
mnfft=mxCreateDoubleMatrix(1,1,mxREAL);
mFs=mxCreateDoubleMatrix(1,1,mxREAL);
mxSetName(T,"T");
mxSetName(mnfft,"mnfft");
mxSetName(mFs,"mFs");
memcpy((char*)mxGetPr(T),(char*)datax, 1024*sizeof(double));
memcpy((char*)mxGetPr(mnfft),(char*)nfft, sizeof(double));
memcpy((char*)mxGetPr(mFs),(char*)Fs,1*sizeof(double));
engPutArray(ep,T); //将转化的参数放入引擎中,此时可在Matlab command窗口下查看此参数
engPutArray(ep,mnfft);
engPutArray(ep,mFs);
engEvalString(ep,"[pxx,fo]=psd(T,mnfft,mFs);"); //利用引擎执行工具箱中文件
engOutputBuffer(ep,buffer,512); //如只想看显示图形,可将返回参数去掉,psd无返回参数缺省情况下会自动画图形
result=engGetArray(ep,"pxx");//取出引擎中的数据放在所指的区域中供后续处理
pPxx=mxGetPr(result);
result=engGetArray(ep,"fo");
pFxx=mxGetPr(result);
engEvalString(ep,"plot(fo,10*log10(pxx));");//利用引擎画图
engEvalString(ep,"title('功率谱分析');");
engEvalString(ep,"xlabel('Hz');");
engEvalString(ep,"ylable('db');");
mxDestroyArray(T); //释放内存
mxDestroyArray(mFs);
mxDestroyArray(mnfft);
mxDestroyArray(result);
engEvalString(ep,"close;");
engClose(ep);
}

  上述程序在Vc下编译需要将 libeng.dll和libmx.dll两个动态库利用以下的命令:

lib/def:<自己的Matlab的安装路径,下同>e:\ Matlab\extern\include\*.def /machine:ix86 /out:*.lib来生成程序所需的静态连接库libeng.lib和libmx.lib,将libeng.lib和libmx.lib所在的目录加入Vc++ project/link/object/library modules下即可。

利用Matlab自身的编译器调用工具箱中的函数
  Matlab的编译器可将Matlab的M文件转换为为C或C++的源代码以产生完全脱离Matlab运行环境的独立的运用程序,但Matlab本身的资料说明编译器如用来建立独立的运用程序,不能调用Matlab工具箱中的函数,这非常不利于搞一些特殊的算法。本人研究了一段时间发现,工具箱中的函数既然是M文件就一定可以用编译器来编译,以提供如Vc的调用函数,但是编译器只能编译一个独立的M文件,即这个 M文件不依赖于其他的M文件。如果M文件中又调用了其他的M文件,可将被调用的M文件拷贝到调用M文件的相应位置,作适当的改动就可以用于编译器编译。编译器不支持图形函数,所以M文件中如有图形函数需注释掉。

  当Matlab的编译器mcc加入适当的参数-e(mcc –e *.*)或-p(mcc –p *.*)就可生成将输入的M文件转换为适用于特定运用的C或C++源代码。这样如果要在Vc下编译通过,还需连入以下几个库libmmfile.dll, libmatlb.dll, libmcc.dll, libmat.dll. libmx.dll. mibut.dll 以及Matlab C MATH库,建议采用前述的方法将动态连接改为静态连接。对于C/C++编译环境的设置,在Matlab command窗口下运行mex –setup 然后依提示操作,而对于C/C++连接环境的设置,运行mbuild –setup依提示操作即可。

  下面给出利用编译器将Matlab工具箱中psd.m文件生成可供Vc调用的函数。

将psd.m文件拷贝一份至Matlab\bin目录下,改写相应调用的M文件如nargchk.m, hanning.m等。为生成的代码简洁,对于采集数据处理输入参数很明了的情况下可作大量的删减,最终使psd.m成为一个不依赖于其他M文件的独立的M文件,注意千万注释掉作图代码,最终改成如下形式,限于篇幅给出关键的几步:
function [Pxx,f]=psd(Fs,nfft,noverlap,x)
window=o,5*(1-cos(2*pi*(1:nfft)’/(nffft+1)));//hanning 窗
dflag=’none’;
window=window(;)
………………………………….

以上只要稍懂Matlab语言和信号处理知识就可完成这项工作。

假设上述代码重新存为testwin.m,在Matlab command 窗口下设置好环境参数运行mcc –e testwin,则可在Matlab\bin下生成testwin.c ,如运行mcc –p testwin 则生成testwin.cpp.
Vc下建立一个基于对话框的文件,然后在对话框里加一个Button控件OnButtonPsd
将上述生成的.c文件的头文件加入到工程的.cpp中,且将#ifdef_cplusplus

extern “c”{
#end if
c代码声明加入Vc的包含文件和生成的.C的包含文件之间
将#ifdef_cplusplus
}
#end if加入.cpp文件未尾

为了简洁且便于处理将生成的c函数稍改动,给出部分代码如下:

void CTestpsdwinDlg::OnButtonPsd(){
mxArray* x_rhs_;//指向采集数据存放区
Fs=23510;//数据采集的频率 nfft=1024;//1024点的fft
double datax[1024]//采集的数据
x_rhs_mxCreateDoubleMatrix(1,1024,mxReal);
memcpy(mxGetPr(x_rhs_),datax,1024*sizeof(double));
noverlap=512;
……………….
……………….
mccCopy(&Pxx,&Spec);
mccCopy(&f,&frevgg_vector);
for(int j=0;j<(int)(nfft/2+1);j++)
{
datap[j]=mccGetRealVectorElement(&Pxx, (j+1));//功率谱密度存于datap[]数组
dataf[j]=mccGetRealVectorElement(&f, (j+1));//相应频率存于数组dataf[]中
}
mccFreeMatrix(&Pxx);
……………….
SendMessageBox(WM_PAINT,0,0);//利用Vc下的图形函数画图
Return;
}

  如上生成的程序可读性不太好,而生成的c++代码则可读性较好,但千万注意只能用 Matlab的MATH库,不可用c++的MATH库,否则编译会出错,限于篇幅在此不述。

3)利用Matcom调用工具箱中的函数

  Matcom编译M文件,先将M文件按照与Matcom的cpp库的对应关系翻译为cpp源代码,然后用对应版本的c编译器将cpp文件编译成相应的exe或dll文件,所以第一次运行要指定c编译器的路径,否则无法编译,指定好的编译信息就写在Matcom\bin\matcom.ini文件中,不过这一步按装matcom时,它自动寻找编译器并将其写入matcom.ini文件中,matcom4.5版中使用TeeChart3.0 OCX控件,因而它支持图形操作。 我们依然用上述的testwin.m文件,不要将图形函数注释掉,利用Mideva来生成可被Vc调用的信号处理程序。

运行Mideva在主界面上直打开M文件,在菜单中选择compile to dll,输入testwin..在Matcom debug目录下可以找到这样的几个文件,testwin.c ,testwin.h,testwin.cpp,testwin.lib,testwin.dll,testwin.exp等。
将上述testwin.cpp和testwin.h加入工程中,project/add to project/files并且在相应的文件中加入”stdafx.h”
加连接库:Tools\option\directory\ , 选include选项,加入e:\matcom45\lib (包含matcom.h)
library选项,加入e:\matcom45\lib

4) project\add to project\files 文件类型选项选(.lib)将e:\matcom45\lib\v4501.lib加入工程中编译运行。相应代码如下:

void CtestmatcomDlg::OnpsdButton(){
double datap[512],dataf[512];
initM(MATCOM_VERSION);//初始化matcom库
Mm Fs,nfft,noverlap;//创建矩阵
Mm x=zeros(1,1024);
Fs=1024;nfft=1024;noverlap=128;
dMm(Pxx_o);dMm(f_o);//创建并命名矩阵
datax[];//数据采集的数据存于此数组中
for(int i=1;i<=1024;i++)
{
x.r(1,i)=datax[i+1];//给x阵赋值
}
testwin(Fs,nfft,noverlap,x,i_o,Pxx,f_o);//matcom生成的函数
for(i=0;i<513;i++){//取出功率谱密度分析结果
dataf[i]=f_o.r(i+1,1);
datap[i]=Pxx_o.r(i+1,1);}
exitM();
return;
}
  可见利用Matcom进行M文件转换非常的容易,生成的代码可读性很好,以上的转换同时生成了可供Vc调用的动态连接库,其使用和一般的动态库一样使用。同时需指明Matcom不仅可转换独立的不依赖于其它M文件的M文件,同时可转换调用其它M文件的M文件嵌套。条件是这此M文件在同一个目录下面,如前所述的psd.m可直接用上述方法转换,生成了多个重载形式的psd函数

  结论: 利用Mtlab引擎调用工具箱中的函数可节省大量的系统资源,应用程序整体性能较好,但不可脱离Matlab 的环境运行。用Matlab编译器进行工具箱函数的调用,须转换相应的M文件使其成为独立的M文件,且不支持图形函数,转换的代码可读性不太好。用Matcom 进行转换非常方便,生成的代码可读性很好,支持图形函数,且代码执行的速度比不转换平均要快 1.5 倍以上。以上程序在Vc++ 6.0, Matlab 5.2, Matcom 4.5 中调试通过,以上方法在工程实践中已得到很好的运用。

通过 VC++ 实现对 MATLAB 的调用


成都 .中国科学院光电技术研究所 邮编:610209 齐 波 董能力

一 引言

  众所周知,MATLAB是功能非常强大的数学软件,它广泛应用于线性代数、自动控制理论、数理统计、数字信号处理、时间序列分析、动态系统仿真等领域。因此如果把MATLAB的程序加入到自己的VC++、VB、POWER STATION及其它语言编制的程序中,将大大减少编程的工作量、保证程序的准确性,并且继承VC++良好的用户界面。下面就以VC++ 为例介绍一下它的外部调用。

二 程序实现

--实现外部调用的思路是:首先将m文件加入到一段C程序中,然后通过MATLAB中的mex批处理文件将该C文件生成exe文件,然后通过VC++的外部调用的函数:shellexec()或winexec()实现调用。 mex批处理文件的调用格式如下:

mex -f msvc60engmatopts.bat *.c
c文件的格式如下:
/* engwindemo.c
* This is a simple program that illustrates how to call the MATLAB
* Engine functions from a C program for windows
* Copyright (c) 1996-1998 by The MathWorks, Inc.
* All Rights Reserved.*/
#include < windows.h >
#include < stdlib.h >
#include < stdio.h >
#include < string.h >
#include < d:\matlab\extern\include\engine.h >
void main()
{
Engine *ep;
/*
* Start the MATLAB engine
*/
if (!(ep = engOpen(NULL))) {
MessageBox ((HWND)NULL, (LPSTR)
"Can't start MATLAB engine",
          , MB_OK);
exit(-1);
}
    engEvalString(ep, "contour(peaks)");
      engClose(ep);
}

- 程序中的黑体部分即为要加入的m文件的位置。这样就生成了与m文件同名的exe文件,通过VC++中的外部调用函数便很容易的实现了对MATLAB的调用。
三 结 论

本文给出了VC++等高级程序语言调用MATLAB中程序(函数)的方法,融合了VC++与MATLAB两种不同的语言,大大提高了编程效率。不失为一种较好的程序设计方法。

Matcom: Matlab 与 C++ 结合的有效途径


林 岚* 周云波 杨玉星 林家瑞
单位:华中理工大学生物工程系(武汉 430074)
*男,24岁,硕士研究生

摘 要 本文首先介绍了当前在国内外十分流行的工程设计和系统仿真软件包Matlab的优缺点,然后展开介绍了Matcom(一种将Matlab与C++结合的工具)的功能及如何使用,最后对其效果进行了总结。   
关键词 Matlab Visual C++ Matcom ECG

1 工程设计软件Matlab特点分析  

  Matlab是MathWorks公司于1982年推出的一套高性能的数值计算和可视化软件。它集数值分析、矩阵运算、信号处理和图形显示于一体,构成了一个方便、界面友好的用户环境。它还包括了ToolBox(工具箱)的各类问题的求解工具,可用来求解特定学科的问题。其特点是:   

(1)可扩展性:Matlab最重要的特点是易于扩展,它允许用户自行建立指定功能的M文件。对于一个从事特定领域的工程师来说,不仅可利用Matlab所提供的函数及基本工具箱函数,还可方便地构造出专用的函数,从而大大扩展了其应用范围。当前支持Matlab的商用Toolbox(工具箱)有数百种之多。而由个人开发的Toolbox则不可计数。
(2)易学易用性:Matlab不需要用户有高深的数学知识和程序设计能力,不需要用户深刻了解算法及编程技巧。
(3)高效性:Matlab语句功能十分强大,一条语句可完成十分复杂的任务。如fft语句可完成对指定数据的快速傅立叶变换,这相当于上百条C语言语句的功能。它大大加快了工程技术人员从事软件开发的效率。据MathWorks公司声称,Matlab软件中所包含的Matlab源代码相当于70万行C代码。   

  由于Matlab具有如此之多的特点,在欧美高等院校,Matlab已成为应用于线性代数、自动控制理论、数理统计、数字信号处理、时间序列分析、动态系统仿真等高级课程的基本教学工具;在研究单位、工业部门,Matlab也被广泛用于研究和解决各种工程问题。当前在全世界有超过40万工程师和科学家使用它来分析和解决问题。   

   然而Matlab自身所存在的某些缺点限制了它的应用范围。
 (1)Matlab是一种解释性语言,因此它的实时效率是相当差的。例如在笔者所从事的一个心电信号处理项目中,采用Matlab语言来处理5分钟的心电数据,在486/66系统机上需要25分钟,而同样的算法,用C语言实现,仅需30多秒。由此可见,Matlab的语言执行效率是比较低的。
 (2)Matlab程序不能脱离其环境运行,因此它不能被用于开发商用软件。
   (3)Matlab程序可以被直接看到

2 Matcom:Matlab与C++的完美结合
  MathTools公司推出的Matcom(可于http://www.mathtools.com处获取)可以说是以上问题的完美解决。它可将Matlab的源代码译成同等功能的C++源码。既保持了Matlab的优良算法,又保持了C++的高执行效率。
  下面将详细介绍如何使Matlab与C++接口。
  运行环境:
      Matcom 3.0
      Matlab 5.1
      Visual C++ 5.0
      中文Windows 95
      PC:486/66   

  (1)首先应设置Matcom所对应的编译器类型、数据类型。
  (2)将你所需要的.M文件翻译成C源码。

  在这里将用一个简单的滤波器Matlab源程序:
  Lin.m  
  
b=[1 2 1];   
a=[1 -1.58 0.64];n=50;   
x=[1, ones(1,n-1)];   
y=filter(b,a,x);   
plot(y./70,'*');   
hold on   
plot(x,'.');   
grid;  

    
下面给出了经Matcom翻译后的C++源程序。Lin.cpp      

M b("b",0,0);   
M a("a",0,0);   
M n("n",0,0);   
M x("x",0,0);   
M y("y",0,0);   
b=(BR(1.0),2.0,1.0);   
a=(BR(1.0),-1.58,0.64);   
n=50.0;   
x=(BR(1.0),ones(1.0,n-1.0));   
y=filter(b,a,x);   
plot(rdivide(y,70.0),TM("*"));   
hold(TM("on"));   
plot(x,TM("."));   
grid();   

(3)Visual C++环境设置。   
在Visual C++中的"Project / Settings / C, C++ / Preprocessor /Additional include subdirectories" 中填入当前Matcom所处的路径.。(如"c:\matcom\")在Visual C++中的工程窗口处" "Project / Add to Project / Files".加入mlibvc0.lib库文件。   
(4)修改C++源代码。  

注意:   
①在头文件处应包含matlib.h.

  #define MLIB 0
  #include "matlib.h."

②在类成员函数CMfcApp::InitInstance中调用. InitM启动对Matlab函数的调用。

  BOOL CEcgstarApp::InitInstance()  
   {  
   TRACE0("initM\n");
   initM(stin,stout,sterr);   
  ......
  }  

③在类构造函数CMfcApp::CMfcApp.中不要调用任何Matlab函数   
④在类构造函数CMfcApp::~CmfcApp中调用exitM结束对Matlab函数的调用。

  CEcgstarApp::~CEcgstarApp()   {     exitM();      }

⑤在程序中合适的位置插入经Matcom翻译过后的C++源码。   
⑥编译工程文件,就可得到你所需的结果。

3 结束语  

  笔者在从事生物医学工程ECG信号的检测与处理项目中,对采用不同方法完成同一任务在代码量和运行时间上进行了比较,如表1、表2所列。

表1 ECG检测核心算法代码量

全部用C++编写 全部用Matlab编写 用Matcom转换后的C编写
约3400行 约400行 约600行

表2 ECG检测核心算法运行时间

全部用C++编写 全部用Matlab编写 用Matcom转换后的C编写
约30秒 约25分钟 约1分钟

 由此可见,采用Matcom进行程序设计,这是一种时间、效率上的极好的折衷,它使得你可以将更多的时间放在项目关键问题的思考上,而不是放在算法的实现和程序的编码中,对于广大工程技术人员的开发工作是十分有益的。

Matlab 和 C++ 接口中函数注册的实现


李江红 韩正之
《计算机应用》COMPUTER APPLICATIONS 2000 Vol.20 No.4 P.18-20

简介:李江红(1970-)男,湖南长沙人,博士研究生,主要研究方向:随机决策以及智能控制;韩正之(1947-)男,上海人,教授,主要研究方向:非线性控制以及智能控制。
单位:李江红(上海交通大学智能工程研究所 上海 200030)
     韩正之(上海交通大学智能工程研究所 上海 200030)

摘 要:函数注册在Matlab和C++接口中起着重要的作用。在介绍函数注册作用的基础上,详细分析了函数注册的过程及应当注意的问题,并给出了一种实现函数注册的最简单的方法。用实例展示了函数注册的具体实现过程。
关键词:Matlab;C++;函数注册
中图分类号:TP317,TP319  文献标识码:A
文章编号:1001-9081(2000)04-0018-03

FUNCTION REGISTERING IN
THE INTERFACE BETWEEN MATLAB AND C++

LI Jiang-hong HAN Zheng-zhi
(Intelligent Engineer Research Institute of Shanghai Jiaotong University, Shanghai 200030 China)

  Abstract: Function registration plays an important role in the interface between Matlab and C++. This paper analyzes the procedure of function registering. Each step of function registering is discussed in detail in the paper, and at the end,an example is given for illustration.
  Key words:Matlab; C++; function registration

1 研究意义

  Matlab为用户提供了丰富的Windows图形界面设计方法,使用户能够在利用其强大数值计算功能的同时设计出友好的图形界面。在编程效率、可读性、可移植性和可扩充性上,Matlab远远优于其它高级编程语言。Matlab能够设计出功能强大、界面优美、性能稳定的高质量程序,它受到了越来越多用户的欢迎。然而作为一种以解释方式运行的高级计算机语言,Matlab程序的执行效率较低。为了解决这一问题,MathWorks公司提供了Matlab和C的接口。MathWorks公司提供的Matlab和C的接口方式共有三种:(a)将Matlab程序编译成MEX文件C或C++文件;(b)在C,C++程序中利用Matlab Engine调用Matlab函数;(c) 在C,C++程序中利用Matlab C Math Library或Matlab C++ Math Library调用Matlab函数。其中通过方式(a)、(b)生成的程序只有在安装了Matlab系统上才能正常运行,而由方式(c) 生成的程序则没有这样的要求,它能够以独立执行程序的形式运行,即使客户没有安装Matlab系统。方式(c)唯一的缺点就是不能利用Matlab中丰富的图形句柄处理函数,但是对于VC++等开发工具而言,这不是一个很严重的问题。因此方式(c)是实现功能和效率兼顾的最好接口方法[2]。

  当用方式(c) 生成应用程序时,一个重要的问题就是那些以函数作为自身输入参数的函数实现问题。众所周知,Matlab提供了一组能完成极值及极值点计算,线性规划,二次规划以及非线性方程求解等数值运算的优化函数[3]。

  在Matlab中使用这组优化函数时,用户定义的待优化函数必须作为输入参数传递给这些函数。在优化函数运行的过程中,系统将最终执行用户定义的待优化函数,并且这一切无须程序员的参与。但是当程序员通过方式(c)在C++程序中调用Matlab C++ Math Library中的优化函数时,事情就变得比较复杂。要利用这些优化函数所提供的数值计算功能,保证用户定义的待优化函数真正得到执行,程序员就必须自己完成对待优化函数的注册。因此,函数注册的研究具有重要的实用价值。

2 Matlab和C++接口中函数注册

  在C++程序中,当调用优化函数等以函数为输入参数的函数时,Matlab C++ Math Library调用Matlab C Math Library中的mlfFeval()来执行作为参数传递的函数。在mlfFeval()中又是通过对转换函数的调用来真正地执行作为参数传递的函数。转换函数和映射表是这一过程能够顺利进行的重要原因。函数注册就是定义函数的转换函数并建立相应的它的映射表。对于Matlab C++ Math Library中的函数,MathWorks公司都定义了它们转换函数并建立了它们的映射表,完成了对它们的注册。但是对于用户自定义的函数,程序员就必须自己完成这项工作。

  函数注册可以分成:(1)定义待注册函数;(2)定义转换函数;(3)建立映射表;(4)初始化映射表等以下四个部分来完成。下面将对各个部分进行详细地介绍。
  (1) 定义待注册函数 待注册函数因用户要求而异。为便于后面的分析,设用户定义的函数为:
  mwArray MyFunc(mwArray x){
  ……//函数体。
  }
  注意待注册函数的输入、输出参数的类型必须是mwArray。mwArray是Matlab C++ Math Library中最基本的数据类,它对应Matlab中的基本编程单位—数组。
  (2) 定义转换函数 转换函数的功能是完成Matlab C Math Library和用户函数之间的翻译。由于它面向Matlab C Math Library,因此必须用C定义。对(1)中定义的待注册函数,其转换函数的一般形式如下(为简单起见,该转换函数忽略了错误处理代码):
 typedef mwArray (*PMYFUNC) (mwArray);
 extern "C" {
  int MyThunk (mlfFuncp pFunc, int nArgOut, mxArray **
         ArgOut, int nArgIn, mxArray ** ArgIn){
   mwArray tmp=mwArray(ArgIn[0],0);
   mwArray Out=(*((PMYFUNC)pFunc)) (tmp);
   ArgOut[0]=Out.FreezeData( );
   return 1
  }
 }
  转换函数共有5个输入参数,以MyThunk为例,pFunc是指向待注册函数的指针;nArgIn、nArgOut, 分别是待注册函数的输入、输出参数的个数; ArgIn、ArgOut,则分别是输入、输出参数构成的数组。其中ArgIn[0]对应第1个输入参数,ArgIn[1]对应第2个输入参数……。ArgOut的情形类似。
  由于转换函数面向Matlab C Math Library,因此它的输入、输出参数中的数组类型只能是mxArray (mwArray是Matlab C Math Library中基本的数据,它对应Matlab中的基本编程单位—数组);而注册的函数又只能接受mwArray类型的参数。因此,在调用注册函数之前必须将mxArray型数据转化为mwArray型数据,如MyThunk中:mwArray tmp=mwArray(ArgIn[0],0); 在转换函数返回之前必须将注册的函数的返回值由mwArray型转化为型mxArray,如MyThunk中:ArgOut[0]=Out.FreezeData()。
  (3) 建立映射表 映射表的作用是把待注册函数的名称映射到指向该函数的指针。映射表中的元素由三元组:函数名称、函数指针、转换函数构成。对(1)、(2)中定义的待注册函数和转换函数,相应的映射表为:
  static mlfFuncTabEnt MfuncTab[]={
  {"MyFunc", (mlfFuncp) MyFunc, MyThunk}
  {0, 0 ,0}
 }
  其中mlfFuncTabEnt是Matlab C Math Library中用于存储函数项的数据结构。{0, 0 ,0}表示映射表的结束。
  (4) 初始化映射表 初始化映射表的方式比较单一。例如对(3)中的映射表,程序段:
  class feval—init{
   feval—init{mlfFevalTableSetup(MfuncTab);}
   static feval—init feval—setup;
  }
  feval—init feval—init::feval—setup;
  将完成其初始化。其中mlfFevalTableSetup是Matlab C Math Library中用于初始化映射表的函数。设置静态对象的目的是保证映射表初始化在系统初始化静态变量时就得以完成。
  从上面的分析可以看出,函数注册的过程比较复杂。不过MathWorks公司注意到了这个问题,它定义并提供了DECLARE—FEVAL—TABLE, EVAL—ENTRY, END—FEVAL—TABLE等一组宏来帮助用户简单、轻松地完成函数注册。例如对于(1)中定义的函数MyFunc,程序段:
  DECLARE—FEVAL—TABLE
   FEVAL—ENTRY(MyFunc)
  END—FEVAL—TABLE
就实现了对MyFunc的注册,它取代了(2)、(3)、(4)中的所有工作。事实上,只要待注册函数的输入参数数目不大于8,输出参数数目不大于5,就能用这组宏来完成函数注册。

3 应用实例

  下面的VC++程序通过调用Matlab C++ Math Library中的fmin(),计算函数
  f(x,y)=ex(4x2+2y2+4xy+2y+1)
  在y=2时,位于区间[-2,2]中使函数值最小的自变量x的值。
#include "matlab.hpp"
#include <stdlib.h>
/***(1) 定义注册函数 ***/
mwArray MyFunc(mwArray x,mwArray y){
 mwArray mResult=exp(x)*(4*x ^ 2+2*y ^ 2+4*x*y+2*y+1);
 return mResult;
}
/***(2) 定义转换函数 ***/
typedef mwArray (*PMYFUNC)(mwArray,mwArray);
extern "C"{
 int MyThunk(mlfFuncp pFunc, int nArgOut, mxArray **
       ArgOut,int nArgIn, mxArray **ArgIn) {
 mwArray tmp1=mwArray( ArgIn[0], 0 );
 mwArray tmp2=mwArray( ArgIn[1], 0 );
 mwArray Out=(*((PMYFUNC)pFunc))(tmp1,tmp2);
 ArgOut[0]=Out.FreezeData();
 return 1;
 }
}
/***(3) 建立映射表 ***/
static mlfFuncTabEnt MFuncTab[] ={
{ "MyFunc", (mlfFuncp)MyFunc, MyThunk },
{ 0, 0, 0 }
};
/***(4) 初始化映射表 ***/
class feval—init {
 feval—init() { mlfFevalTableSetup( MFuncTab ); }
 static feval—init feval—setup;
 };
feval—init feval—init::feval—setup;
/***可以取代(2)(3)(4)的宏注册函数 ****/
/***  DECLARE—FEVAL—TABLE   ***/
/***   FEVAL—ENTRY(MyFunc)  ***/
/***  END—FEVAL—TABLE     ***/
/*****    主程序      *****/
void main(void){
 mwArray mStart,mEnd,mP,mOption;
 mStart=-2,mEnd=2;mP=2.0;
 mwArray mResult=fmin("MyFunc",mStart,mEnd,mOption,mP);
 cout < < mResult< < endl;
 }

参考文献

[1] 薛定宇. 控制系统计算机辅助设计—MATLAB语言及应用[M].北京:清华大学出版社, 1996.17-19.
[2] MATLAB C++ math Libarary User′s Guide[Z]. The Math Works, Inc., 1998.2-18.
[3] 施阳,李俊.MATLAB语言工具箱—TOOLBOX实用指南[M].西安:西北工业大学出版社,1998.100-101.

收稿日期:1999-10-19

Matlab 与 C++ 混合编程


   Matlab是当今世界上使用最为广泛的数学软件,它具有相当强大的数值计算、 数据处理、系统分析、图形显示,甚至符号运算功能,是一个完整的数学平台,在这个平台上,你只需寥寥数语就可以完成十分复杂的功能,大大提高了工程分析计算的效率。另外由于Matlab的广泛使用,于是出现了为各个领域专门使用的工具箱(即在某一研究领域常用数学工具的函数包),这些工具箱的出现更加促进了Matlab的流行。

  Matlab强大的功能只能在它所提供的平台上才能使用,也就是说,你必需在安装有matlab系统的机器上使用.m文件,这样就给工程计算带来了很大不便;特 别是,在matlab中,使用的行解释方式执行代码,这样大大地限制了代码执行速度。 于是人们想到,能否开发一个matlab与其他高级语言的接口,这样就可以把matlab 的强大功能融入各种应用程序中,并且通过高级语言编译器编译为2进制代码, 从而大大提高了执行速度。

  于是matlab的5.1版本提供了自带的C++ Complier,同时MathTools公司也为 Matlab开发了m文件高效解释和调试IDE:MIDEVA。经过近两年的发展,matlab 5.3 中的C complier--mcc版本已经为2.0,而MIDEVA最新版本为4.5。

  将matlab与C混合编程大概有如下三种方法:

用Matlab的mcc将.m文件翻译为cpp源文件,然后在C编译器中调用 也可以用mcc编译编译为stand-alone程序。

用Matcom(MIDEVA)将.m文件翻译为cpp代码,并编译为exe或dll 文件。

按照matcom的语法,在VC或BCB中直接书写matlab语句(与matlab 很相似),这也是我最喜欢用的方法。

  方法1和2/3各有利弊,1不支持图形(支持图形的库国内现在还没有D), 1对类支持也不够,2支持绝大多数的matlab语句(包括图形),但对于struct等的支持也有缺陷。

   MATLAB5.3的提供了C/C++数学库,其中的C++数学库功能很强,使用它可以用类似MATLAB 的语法编写C++程序,十分方便。虽然速度上仍然比手工C/C++程序慢,但是由此换来的高效的开发效率和可靠性往往是值得的。另外mcc命令可以将M文件转化为C或CPP文件,编译后可以脱离MATLAB运行 ,它们也是使用的C/C++数学库。 不过,MATLAB的数学库在开发时似乎倾向于编译独立的可执行程序,把VC、BC只是作为一个编译和连接的工具,而没有过多地考虑在VC、BC的集成环境下进行开发。这给我们带来了不便。 很多网友问起如何将MCC生成的C++程序嵌入到VC。最近对这个问题做了一下尝试,在这里做一个总结,请大家回去试试。这里只介绍VC的情况,用BC的网友就自己研究研究吧估计是类似的。

1、设置项目编译选项 首先建立一个新的项目,或者打开一个已有的项目,然后选择菜单
Project->Settings->C/C++
Category:Preprocessor
Preprocessor definitions:
添加: MSVC,IBMPC,MSWIND
Category: Precompiled Headers
选择: Automatic use of precompiled headers
Through header: stdafx.h

2、调设置项目连接选项
首先要从下面几个函数定义文件(*.def)生成相应的导入库文件(*.lib)
libmmfile.def
libmcc.def
libmatlb.def
libmx.def
libmat.def
它们位于目录c:\matlab\extern\include
用下面命令导出库文件:
lib /def:libmmfile.def /out:libmmfile.lib /machine:ix86
lib /def:libmcc.def /out:libmcc.lib /machine:ix86
lib /def:libmatlb.def /out:libmatlb.lib /machine:ix86
lib /def:libmx.def /out:libmx.lib /machine:ix86
lib /def:libmat.def /out:libmat.lib /machine:ix86
将它们放入你的项目连接选项
Project->Settings->Link
Category:Input
Object/library modules:
添加:libmatpm.lib libmmfile.lib libmcc.lib libmatlb.lib libmx.lib libmat.lib
注1: 中间是空格,不要加逗号
注2: libmatpm.lib是C++ MathLib的库,如果是只用C,就不用连接它了。
Ignore libraries:
添加: msvcrt.lib 注:
仅在Debug版本中需要。原因不明.

3、设置编译环境
Tools->Options->Directories
Include fiels添加:
c:\matlab\extern\include
c:\matlab\extern\include\cpp
Library fiels添加:
c:\matlab\extern\lib
c:\matlab\extern\include
注:下面这个目录是那些lib所在的地方。 如果都挪到上面的目录,这个自然就不要了。

4、编写程序
用MCC命令生成的CPP文件和自己手工编写的CPP文件, 其项目设置是完全相同的, 程序的语法原则上也是一样的. 只是MCC生成的CPP文件有大量"没用"的代码.
(1)添加自己编写的程序模块
用下面文件头:
#include "stdafx.h"
#include "matlab.hpp"
然后, 按照C++ MathLib文档要求的格式书写程序.
(2)嵌入mcc生成的文件 在MATLAB下用下面格式的命令生成cpp文件 mcc -t -L Cpp test 得到test.hpp, test.cpp 将test.cpp加入项目, 不做任何改动. 最后,摁F7编译就可以了。

5、实例
上面罗罗嗦嗦一大堆,肯定让你打哈欠了:没关系,其实还是挺简单的,这里有个VC的project,用C++数学库解决了一个幼儿园的算术题. 这里说明一下:
MATLAB程序test.m:
function [sum, prod]=test(x,y)
sum = x+y;
prod = x*y;
用来计算两个数的和与积。注意这是两个返回变量的情况。 另外,x,y当然可以是数组。 用mcc命令生成了test.hpp和test.cpp。
文件demo.cpp:
#include "stdafx.h"
#include "matlab.hpp"
#include "test.hpp"
void mccDemo()
{ CString str;
mwArray x, y, sum, prod;
double dx,dy,dsum,dprod;
x = 5.0;
y = 10.0;
sum = test(&prod, x, y);
dx=x(1,1);
dy=y(1,1);
dsum=sum(1,1);
dprod=prod(1,1);
str.Format("%f+%f=%f\n%f*%f=%f", dx,dy,dsum,dx,dy,dprod);
AfxMessageBox(str); }

  由于采用了C++数学库,语法很简单。注意数组的赋值、其中数据的存取,这些都要仔细地看手册。 最后是跳出一个消息框,显示计算结果。

VC++ 中编译 MEX 程序


1. 目的

  一般情况下,我们都是在MATLAB命令行或DOS命令行下编译MEX程序。 所用的命令就是:mex filename.c 这有很多不方便的地方:
  a. 虽然mex也可以编译C++的mex程序,但是它的主框架仍是C的 a. 当程序有多个模块时,需要多次使用mex命令,操作很麻烦
  b. 不能利用VC特有的ClassWizard自动创建和维护类
  c. 不能用MFC类库编写应用程序
  d. 不能方便地进行类似VC的项目管理
这样一个命令行的编程环境可能会大大束缚你的想象力... 其实解决问题的方法很简单,下面以VC6环境为例详细解说如何在IDE中编译MEX程序。

2. 步骤

(1)准备工作 (这里假设MATLAB的路径为:C:\MATLAB)
a. 由matlab.def创建库文件matlab.lib
进入c:\matlab\extern\include目录,用lib命令生成matlib.lib
  C:\>cd \matlab\extern\include
  C:\MATLAB\extern\include>lib /def:matlab.def /out:matlab.lib
  Microsoft (R) Library Manager Version 6.00.8447
  Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
  LIB : warning LNK4068: /MACHINE not specified; defaulting to IX86
  Creating library matlab.lib and object matlab.exp
  C:\MATLAB\extern\include>
  警告信息不用理它。
b. 修改mex.h
该文件也在C:\MATLAB\extern\include目录
找到这一行:void mexFunction(
替换为 : __declspec( dllexport ) void mexFunction(
另保存为 :mex_vc.h
(2)启动VC,创建MFC AppWizard(dll)程序框架
(3)设置系统选项
a. 设定头文件和库文件路径
菜单Tools->Options->Directories
Include files:添加c:\matlab\extern\include
Library files:添加c:\matlab\extern\include
b. 设置编译连接选项
菜单Project->Settings
C/C++->Preprocessor definitions: 添加MATLAB_MEX_FILE
Link->Object/Library modules: 添加matlab.lib
注1. 其它优化、调试以及头文件、库文件等选项看情况调整
注2. Release版在General->Microsoft Foundation Classes选项中, 必须设置为Use MFC in a Static Library. 原因不明 (4)编写DLL主程序
a. 文件名要用.cpp为扩展名,头两行必须是:
#include "stdafx.h"
#include "mex_vc.h"
b. 编写mexFunction函数,用如下格式声明:
void mexFunction(
int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
...
}
(5) 象普通VC的DLL程序一样继续编写其它部分程序,加入其它模块
(6) 调试错误,用F7编译,生成DLL。我的例子:

--------------------Configuration: MatWave - Win32 Release--------------------
Compiling resources...
Compiling...
StdAfx.cpp
Compiling...
MatWave.cpp
sample.cpp
mex.cpp
Generating Code...
Linking...
Creating library Release/MatWave.lib and object Release/MatWave.exp
LINK : warning LNK4089: all references to "SHELL32.dll" discarded by /OPT:REF
LINK : warning LNK4089: all references to "comdlg32.dll" discarded by /OPT:REF
MatWave.dll - 0 error(s), 2 warning(s)

大功告成,有两个警告,不理它。 拿到MATLAB里面试试吧,不过要有非法操作和异常退出的心理准备哦.
另外:如果要在MATLAB运行中重新编译DLL,需先执行: clear mex

C++Builder与Matlab混合编程的实现


 长沙 张云洲

 在C++Builder中调用Matlab工具箱函数,有两种实现方式。一种是基于Matlab环境支持,通过必要的设置实现;笔者在本刊上曾撰文对这种方式进行了专门的阐述。另一种则是完全脱离Matlab环境,通过动态连接库方式实现对Matlab工具箱函数的调用,这可以通过一种开发平台Mediva来实现。相对来说,前者的限制因素较多,而后者则较为方便灵活。

一、Mediva软件平台

  Mediva是Mathtools公司推出的一种Matlab编译开发软件平台,提供对Matlab程序文件(M文件)的解释执行和开发环境支持。该软件有为Borland C++、Visual Basic和Dephi等编程语言开发的不同版本,目前其版本已经到了4.5版。软件大小仅6.5M,可以通过访问其站点www.mathtools.com免费下载试用一个月。 Mediva软件平台本身的功能相当强大,提供近千个Matlab的基本功能函数,通过必要的设置,就可以直接实现与C++的混合编程,而不必再依赖Matlab;同时,Mediva还提供编译转换功能,能够将Matlab函数或编写的Matlab程序转换为C++形式的DLL,从而实现脱离Matlab环境对Matlab函数和过程的有效调用,这样就有可能实现对Matlab强大的工具箱函数的利用。

  Mediva的缺点是C++与Matlab混合编写的应用软件必须携带必要的DLL,从而增大了软件的体积(约4M),同时也不能对所有的Matlab函数提供支持,例如采用类库进行设计的部分函数。但尽管如此,对于控制系统计算机设计、分析的工作来说,Mediva仍不失为一个好的工具。

  由于利用Mediva将Matlab工具箱函数转换成DLL的内容较多,限于篇幅本文在此仅给出对Matlab函数直接调用的实现,而将另撰文阐述DLL的实现。

二、C++Builder直接调用Matlab函数

  本文假设已经安装了Mediva软件或已经得到必要的两个动态连接库mdv4300.dll和ago4300.dll。

  Mediva提供的近千个Matlab基本功能函数,都可以在C++Builder中直接调用。这些函数包括基本的操作、命令、I/O、线性代数、位图、控制等,基本上可以满足我们的一般需要。当然其最大的优点就是可以直接在C++Buider中直接调用而不必考虑安装庞大的Matlab。

  其实现方式和步骤如下:

  1.Lib文件的生成

  在Dos下用C++Builder中的Implib.exe,通过如下命令生成mdv4300.lib: implib mdv4300.lib mdv4300.dll

  将上述两个DLL文件和此Lib文件拷贝到当前目录下。

  2.实现与Matlab的混合编程

  Matlab.h包含了Mediva中所有类型、常量、函数的说明和定义,必须将此头文件放于程序的第一行。Mediva给出的Matlab函数形式并不特殊,如绘线函数Plot,在Mediva中说明为:Mm DLLI plot(cMm varargin);varargin与Matlab 中的意义是一样的,与输入变量的个数相对应。所有可以直接使用的函数都在Matlib.h头文件中定义,而在mdv4300.dll中实现。

  但在C++Builder中使用Mediva提供的Matlab函数的格式,与Matlab编程稍有不同,这主要体现在C++中必须进行必要的说明上。例如我们要用绘线函数Plot来绘制数组x[100]的红色图线。在Matlab中调用为Plot(x,'r');在C++中调用则为:Plot(CL(x),TM("r")),其中CL是一个关键字,是多变量输入时所必须使用的,用以指明调用的变量;而TM则指明,这是一个字符。

  下面我们给出一个示例程序,其功能是对一个1024点的输入数组进行FFT 变换,并绘制变换后频谱实部的火柴杆图,最后将原数据和变换后的数据写入数据文件中。

#include "matlib.h"
//必须包含的头文件
#include < vcl.h >
#pragma hdrstop

#include "TryMatcomU.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int k=0;
    initM(MATCOM_VERSION); //必须进行的初始化
    Mm cur1,cur2; //定义变量
    cur1=zeros(128); cur2=zeros(128); //变量初始化

    for (k=1;k< =128;k++)
   cur1.r(k)=randM(); //生成一个随机数列
    figure(1);
    plot(cur1);//图形显示该数列
    
   cur2=fft(cur1,128); //做128点fft变换
   figure(2);
   //绘制fft变换后实部的火柴杆图,注意此处多变量输入的格式
   stem((CL(cur1),real(cur2),TM("r")));
   
fid=fopen(filename,mode,format) opens
   exitM(); //退出调用
}

  如果完全使用C++来实现本程序的工作,其代码将超过300行!由此可以看出,C++Builder与Matlab函数的混合编程可以给我们带来多么大的方便!
  3.变量内部状态/数据的观察方法

  Mediva使用的所有变量均定义为Mm类型。如果在C++Builder中观察Mm类型变量的内部状态/数据,要稍麻烦一些。但在调试程序时,这又是不可避免的一步,这里举例给出变量观察的方法。

  例如对上面生成的cur2数列进行观察,
  *cur2.pr 0.1892 cur2(1)的实部
  *cur2.pi 0.0013 cur2(1)的虚部

三、C++Builder调用Matlab工具箱函数转换后的DLL

  1.Matlab函数向DLL的转化

  Mediva软件提供了将Matlab函数转换为DLL的功能,非常方便。但需要注意的是:

  1.Matlab5.0以上版本,所有带有tf类的函数均无法转换;

  2.Matlab4.2以下版本,多数函数能够转换,但转换后大多不能直接使用,而必须加以处理。

  MATCOM V4.3中把含有输入参数的M文件转换成DLL时,生成的DLL无法调用.以.M为例,

  function [x1,x2]=flower(x3)

  MATCOM生成的FLOWER.CPP和FLOWER.H中声明为:

Mm flower(Mm x3, i_o_t, Mm& x1__o, Mm& x2__o) {
begin_scope
x3.setname("x3");
...
   }

Mm flower(Mm x3);
Mm flower(Mm x3, i_o_t, Mm& x1__o, Mm& x2__o);
而生成的G_FLOWER.CPP声明为:

---- void DLLX _stdcall flower_1_1(Mm** in01, Mm **out01)

---- void DLLX _stdcall flower_1_2(Mm** in01, Mm **out01, Mm **out02)

---- 其中对于in01的说明是不正确的.应按如下修改。然后,按如下MAKE文件进行编译。

#
# MATCOM makefile
#

all: flower.dll

g_flower.obj: g_flower.cpp
bcc32 -c -Id:\matcom43\ -WD -Id:
\matcom43\lib -H=matlib.csm -a4
-5 -eg_flower.obj g_flower.cpp

flower.dll: flower.obj g_flower.obj
bcc32 -Ld:\matcom43\ -WD -Id:
\matcom43\lib -H=matlib.csm -a4 -5 -eflower.dll
@flower.rsp d:\matcom43\lib\mdv4300b.lib

  在CPP中调用这个函数之前,一定要先给in01分配空间。
  #include "matlib.h"
  #pragma hdrstop
  
  #include "flower.h"
  
  #define WIN32_LEAN_AND_MEAN
  #include < windows.h >
  #include "matlib.h"
  #pragma hdrstop
  
  extern "C" {
    void DLLX _stdcall flower_1_1(Mm in01, Mm **out01) {
*out01=new Mm();
//*in01=new Mm();
**out01=flower(in01);
exitM();
    }
    
    void DLLX _stdcall flower_1_2(Mm in01, Mm **out01, Mm **out02) {
*out01=new Mm(); *out02=new Mm();
//*in01=new Mm();
flower(in01, i_o , **out01, **out02);
exitM();
    }
    

VC++调用MATLAB Engine

实验环境: Pwin98, MATLAB5.3, Visual C++ 6.0 SP3
   1 生成几个连接库文件
   在目录 c:\matlab\extern\include 下找这几个文件:
   libeng.def libmx.def libmat.def
   在DOS行命令下生成三个库文件:
   c:\msdev\bin\lib /def:libmat.def /out:libmat.lib
   c:\msdev\bin\lib /def:libeng.def /out:libeng.lib
   c:\msdev\bin\lib /def:libmx.def /out:libmx.lib
   把生成的三个LIB文件加入你的LINK项
   libeng.lib libmx.lib libmat.lib
   2 写包含engine功能的文件
   可以参考:
   c:\matlab\extern\examples\eng_mat\engwindemo.c
   注意: 可以用.CPP做后缀. 比如:MATLAB.CPP
   3 设置编译选项
   把MATLAB.C加入你的PROJECT
   MATLAB.C的开头加入两行:
   #INCLUDE "stdafx.h"
   #INCLUDE "engine.h"
   包含其它头文件, 自己看情况而定
   4 编写调用engine的函数
   比如响应菜单调用自己的函数matlabPlot,而此函数的实现在matlab.cpp文件中实现,内容如下:
   #include "stdafx.h"
   #include "engine.h"
   int MatlabDraw()
   {
   Engine *ep;
   if (!(ep = engOpen(NULL))) {
   MessageBox ((HWND)NULL,"Can't start MATLAB engine","Matlab Draw",MB_OK);
   return 0;
   }
   engEvalString(ep, "x=0:0.01:2*pi;y=sin(x);plot(x,y);");
   engEvalString(ep, "title('Matlab Drawing');");
   engEvalString(ep, "xlabel('X');");
   engEvalString(ep, "ylabel('Y');");
   MessageBox(NULL, "Press any key …","MATLAB Draw",MB_OK);
   engClose(ep);
   return 1;
   }
   5 设置其它编译连接选项
   PreProcessor 加上: MATLAB_MEX_FILE
   INCLUDE 加上: c:\matlab\extern\include
   LIB 加上: c:\matlab\extern\include
   最后,如果有必要的话,在DOS命令行运行: MATLAB /regserver
(责任编辑:泉水)
顶一下
(29)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片