本文主要介绍VC与Matlab混合编程的两种方法,并详细介绍了VC向Matlab传递复杂数据:结构体的方法,有详细代码说明。html
Matlab 主要面对科学计算、可视化以及交互式程序设计的高科技计算环境,但因为Matlab开发平台上开发的程序不能脱离Matlab运行环境,于是在处理一些实际应用问题时显得灵活性不足,而VC++则在必定程度上可以弥补这一漏洞,所以,将两者结合共用,各献其长,能够为科研工做和工程开发提供更为强大的技术支持。c++
Matlab做为控制系统设计的一种通用工具,它能够很方便的和VC进行链接。通常而言,Matlab与VC混合编程的实现方法有不少种,这里主要介绍如下两种:编程
1 VC调用Matlab Engine的方式:数组
Matlab Engine是一组Matlab提供的接口函数,支持C/C++语言,Matlab Engine采用C/S(客户机/服务器)模式,Matlab做为后台服务器,而C/C++程序做为前台客户机,向Matlab Engine传递执行命令和数据信息,从Matlab Engine接收执行结果。用户能够在前台应用程序中调用这些接口函数,实现对Matlab Engine的控制。采用这种方法几乎能利用Matlab所有功能,可是须要在机器上安装Matlab软件,缺点是执行效率较低。下面简单介绍下这种方式的实现步骤:服务器
1.1 API接口介绍数据结构
先来介绍一组Matlab提供的引擎API接口:(仅做简单功能介绍,详细参数说明请参考Matlab帮助)函数
Engine* engOpen(const char* startcmd)启动Matlab引擎工具
int engClose(Engine* ep) 关闭Matlab引擎ui
int engEvalString(Engine* ep, const char* string)执行Matlab表达式url
mxArray* engGetArray(Engine* ep, const char* name)获取一个变量数组的值
int engPutArray(engine* ep, const mxArray* mp)设置一个变量数组的值
int engPutVariable(Engine *ep, const char *name, const mxArray *pm)同上
mxArray *engGetVariable(Engine *ep, const char *name)获取一个变量
int engOutputBuffer(Engine* eP,char* p,int n)获取输出字符串
1.2 VC环境配置
要想在VC集成环境下调用Matlab引擎实现VC和Matlab的混合编程,通常须要通过如下几个必要的步骤:(如下以Matlab2008a和VS2005版本为例)
(1)添加include路径:将“\extern\include”(在Matlab的安装路径下)路劲添加到VC编译器的include下
(2)添加lib路径:将“\extern \lib\win32\microsoft” (在Matlab的安装路径下) 路径添加到VC编译器的lib下
(3)加载lib:须要加载至少libmx.lib、libmat.lib、libeng.lib三个库
(4)include头文件:在要使用 engine 函数的地方包含engine.h头文件
1.3 引擎调用
接下来就能够在VC中调用Matlab引擎了,简单的示例代码以下:
#include "engine.h"
#pragma comment(lib, "libeng.lib")
#pragma comment(lib, "libmx.lib")
#pragma comment(lib, "libmat.lib")
void TestDeno()
{
Engine* pEng = NULL;
if (!(pEng = engOpen(NULL)))
{
printf("Open matlab enging fail!");
return;
}
//call Engine plot A*sin(t)+B A=2 B=1
mxArray *A = NULL;
double init = 2;
A = mxCreateDoubleMatrix(1, 1, mxREAL);
memcpy((void*) mxGetPr(A), (void*)&init, sizeof (double));
engPutVariable(pEng, "A", A);
init = 1;
memcpy((void*) mxGetPr(A), (void*)&init, sizeof (double));
engPutVariable(pEng, "B", A);
mxDestroyArray(A);
Sleep(3*60*1000);
engEvalString(pEng, "t=0:0.2:7;plot(t,A*sin(t)+B);");
if(NULL != pEng)
{
engClose(pEng);
}
}
示例代码经过VC调用Matlab引擎,绘制正弦曲线,相对简单,就再也不详细解释,效图以下:(注意这里A、B的赋值方式,后续还会说起)
2 VC调用Matlab DLL的方式:
DLL是一个可执行的二进制文件。把不少通用的功能放在DLL中,能够供各类应用程序调用,这样能够很好的减小外部存储空间的占有量,并实现代码的共享。Matlab也支持将m程序编译成dll,供其余语言(包括VC、VB、Fortran等)调用,下面先简单介绍下Matlab DLL的编译步骤:
2.1 Matlab DLL制做
(1)配置Matlab的编译器跟,要将mex和mbuil两个都配置成本机上安装的VC,关于mex编译器的配置参见上一篇博文《S-Function实现simulink》,mbuild的配置方法同mex
(2)编译m函数成为dll,在matlab命令空间中使用mcc命令编译m文件:(mcc的使用参考Matlab帮助)
示例:将一个Matlab函数MyFun 编译成libMyFun 的命令:
mcc –W cpplib:libMyFun –T link:lib MyFun
参数说明:-W控制编译以后的封装格式,cpplib表示c++的lib,冒号以后是编译输出lib名,-T表示目标,link:lib表示链接到lib的目标,MyFun是待编译的m文件名,编译成功以后,会输出三个文件:libMyFun.lib libMyFun.dll libMyFun.h
2.2 DLL在VC中调用
要想在VC集成环境下调用Matlab的DLL,实现VC和Matlab的混合编程,通常须要通过如下几个必要的步骤:(如下以Matlab2008a和VS2005版本为例)
(1)VC环境配置,同前面VC调用Matlab引擎方式相似,也须要配置VC的编译环境,其中include路径、lib路径和前一种方式相同,加载的lib变动为:mclmcrrt.lib、libmx.lib、libmat.lib、mclmcr.lib四个,include的头文件变动为:mclmcr.h、matrix.h、mclcppclass.h三个。
(2)DLL中函数的调用,先看一下m函数编译成C++Dll以后的函数声明,例:matlab函数:function [y, out] = CaculateFun(x, in)编译以后对应c++的声明:void MW_CALL_CONVCaculateFun(int nargout, mwArray& y, mwArray& out, const mwArray& x, const mwArray& in)参数int nargout指定调用时,输出参数的个数,紧跟的后续nargout个参数y、out为输出参数,后续再剩下的多个参数x、in就为输入参数。
(3)注意事项:在VC中调用dll中的函数以前,须要先调用函数libFunInitialize初始化,在调用完函数之后,须要再调用函数libFunTerminate和mclTerminateApplication终止。
准备好了上述步骤,就能够在VC中调用DLL中函数的了,简单的示例代码以下:
// include matlab sys head file
#include "mclmcr.h"
#include "matrix.h"
#include "mclcppclass.h"
// include lib head file
#include "libCaculateFun.h"
// link matlab sys lib
#pragma comment(lib, "mclmcrrt.lib")
#pragma comment(lib, "libmx.lib")
#pragma comment(lib, "libmat.lib")
#pragma comment(lib, "mclmcr.lib")
// link lib
#pragma comment(lib, "libCaculateFun.lib")
void TestDeno()
{
// init lib
if (!(libCaculateFunInitialize()))
{
std::cout<<"Could not init lib !"<<endl;
return -1;
}
double xxxx[2] = {0};
double inin = 0;
double yyyy[2] = {0};
double outo = 0;
// 为函数参数分配内存空间
mwArray mwXX(1, 2, mxDOUBLE_CLASS);
mwArray mwIn(1, 1, mxDOUBLE_CLASS);
mwArray mwYY(1, 2, mxDOUBLE_CLASS);
mwArray mwOut(1, 2, mxDOUBLE_CLASS);
// 为输入参数赋值
mwXX.SetData(&xxxx, 2);
mwIn.SetData(&inin, 1);
// 调用计算函数
CaculateFun(2, mwYY, mwOut, mwXX, mwIn);
// 获取输出参数
outo = mwOut.Get(1, 1);
// lib Terminate
libCaculateFunTerminate();
// MCR Terminate
mclTerminateApplication();
}
示例代码经过VC调用Matlab DLL,将变量xxxx的值付给yyyy,将变量inin的值付给outo,相对简单,就再也不详细解释,(注意这里函数参数的赋值方式,后续还会说起)
3 Vc向Matlab函数传递参数
前面在介绍两种混合编程的方式时,示例代码中已经涉及到了参数的传递问题,对于VC中的简单数据类型:变量、数组等,网上已经有不少资料介绍,前面示例代码中也有所体现,这里再也不详细说明。
关于复杂数据结构:用户自定义结构体类型,处理起来比较麻烦,网上能找到的资料也较少,因为前面说起的两种方式,在传递参数时,使用的数据类型是不一致的:引擎方式只能是mxArray类型,而DLL方式的参数只能是mwArray,所以须要分别介绍将结构体转换成这两种类型的方法:
先来定义两个结构体:
// 三维坐标系中的点
struct Postion
{
double x;
double y;
double z;
};
// 名字标示的一个坐标点
struct Coordinate
{
struct Postion pos;
char name[ARRAYSIZE];
};
分别介绍将如上两个结构体转成Matlab能使识别的Array的方法
(1) 结构体转换成mxArray:解释比较多余,就直接上代码吧:
mxArray *Staruct2mxArray(struct Postion *pStaruct)
{
mxArray *pm, *mx;
mwSize m = 1, n = 1;
const int nfields = 3;
const char *fieldnames[3] = {"x","y","z"};
const mwIndex index = 0;
pm = mxCreateStructMatrix(m, n, nfields, fieldnames);
mx = mxCreateNumericMatrix(m, n, mxDOUBLE_CLASS, mxREAL);
memcpy(mxGetPr(mx), &(pStaruct->x), sizeof(double));
mxSetFieldByNumber(pm, index, 0, mx);
mx = mxCreateNumericMatrix(m, n, mxDOUBLE_CLASS, mxREAL);
memcpy(mxGetPr(mx), &(pStaruct->y), sizeof(double));
mxSetFieldByNumber(pm, index, 1, mx);
mx = mxCreateNumericMatrix(m, n, mxDOUBLE_CLASS, mxREAL);
memcpy(mxGetPr(mx), &(pStaruct->z), sizeof(double));
mxSetFieldByNumber(pm, index, 2, mx);
return pm;
}
mxArray *Staruct2mxArray(struct Coordinate *pStaruct)
{
mxArray *pm, *mx;
mwSize m = 1, n = 1;
const int nfields = 2;
const char *fieldnames[2] = {"pos","name"};
const mwIndex index = 0;
pm = mxCreateStructMatrix(m, n, nfields, fieldnames);
mx = Staruct2mxArray(&(pStaruct->pos));
mxSetFieldByNumber(pm, index, 0, mx);
n = ARRAYSIZE;
mx = mxCreateNumericMatrix(m, n, mxCHAR_CLASS, mxREAL);
memcpy(mxGetPr(mx), pStaruct->name, sizeof(char)*ARRAYSIZE);
mxSetFieldByNumber(pm, index, 1, mx);
return pm;
}
(2) 结构体转换成mwArray:
mwArray Staruct2mwArray(struct Postion *pStaruct)
{
mwSize m = 1, n = 1;
const int nfields = 3;
const char *fieldnames[3] = {"x","y","z"};
mwArray pm(m, n, nfields, fieldnames);
mwArray mx(pStaruct->x);
pm(fieldnames[0], 1, 1) = mx;
mwArray my(pStaruct->y);
pm(fieldnames[1], 1, 1) = my;
mwArray mz(pStaruct->z);
pm(fieldnames[2], 1, 1) = mz;
return pm;
}
mwArray Staruct2mwArray(struct Coordinate *pStaruct)
{
mwSize m = 1, n = 1;
const int nfields = 2;
const char *fieldnames[2] = {"pos","name"};
const mwIndex index = 0;
mxArray pm(m, n, nfields, fieldnames);
mxArray mpos = Staruct2mwArray(&(pStaruct->pos));
pm(fieldnames[0], 1, 1) = mpos;
mxArray mname(pStaruct->name);
pm(fieldnames[1], 1, 1) = mname;
return pm;
}
(3) mxArray转换成结构体:
struct Coordinate *mwArray2Staruct(mwArray pm)
{
// 代码仅处理 name 字段 pos 字段是三个简单类型省略
struct Coordinate *pStaruct = new struct Coordinate;
const int nfields = 2;
const char *fieldnames[2] = {"pos","name"};
mwArray mwStr = pm.Get(fieldnames[1], 1, 1);
char *str = strdup(mwStr.ToString());
memcpy(pStaruct->name,str,sizeof(char)*ARRAYSIZE);
return pStaruct;
}
(4) mwArray转换成结构体:
struct Coordinate *mxArray2Staruct(mxArray *pm)
{
// 代码仅处理 name 字段 pos 字段是三个简单类型省略
struct Coordinate *pStaruct = new struct Coordinate;
mxArray * mxname = mxGetFieldByNumber(pm, 1, 1)
mxArray * str = mxGetPr(mxname);
memcpy(pStaruct->name,str,sizeof(char)*ARRAYSIZE);
return pStaruct;
}