from: http://www.simwe.com/forum/thread-801187-1-1.html

程序中使用MATLAB编译产生的DLL (VS2005, MATLAB7.5, mwArray)

最近有几个帖子都在讨论有关在C++程序中使用MATLAB编译产生的动态链接库DLL。本 来想用原来帖子中给出的m代码作为例子,无奈那段代码其实无法执行。为了避免分散注意力,也为了更好地讨论问题,于是决定还是另外新开一个主题,以简单的 例子,和大家一起讨论如何在C++程序中使用matlab编译产生的DLL。

同时MATLAB最新版本R2007b刚刚发布不久,其中包含编译器4.7版,有了一些新的特性,于是也借这个例子来验证一下相关的技术有没有发生大的变化。

最近讨论的问题是关于在C++程序中调用MATLAB编译产生的动态链接库。具体的问题是在 调用DLL中函数时传递参数。我们知道,MATLAB可以把m代码编译成两种DLL,分别是C语言接口和C++语言接口的DLL。论坛上的问题是关于调用 C++接口的DLL中的函数,所以这里我们就以这种DLL为例来介绍。在C++接口的DLL中,函数的输入、输出参数都是mwArray对象。所以问题归 结为如何在C++程序中使用mwArray。

这个问题本来可以只用一个简单的C++程序来练习,而不需要MATLAB编译的DLL。不过为了模拟实际情况,选择以一个更为接近实际的m代码作为例子。在这个例子中,输入输出都是矩阵,至于标量,它也是一个一行、一列的矩阵。

实验环境:Windows 2003 Std Server with SP2 (En),MATLAB R2007b,Visual Studio 2005。MATLAB安装路径:h:MATLABR2007b。

实验内容:把一个简单的m代码编译成C++接口的DLL,然后在C++程序中调用。为了简单起见,这里的C++程序是一个Win32 Console程序,而不是Windows图形界面的程序,不过不妨碍我们的讨论。

下面是这个例子用到的m代码。它定义了一个名为myadd2的函数:

[Copy to clipboard] [ - ]

CODE:

function [y,z] = myadd2(a, b)

% dummy function, just to demonstrate the idea

y = a+b;

z = a+2*b;

end

首先把这个函数编译成C++接口的DLL。为此,我们需要先设置MATLAB编译器。具体做法是在MATLAB命令行执行“mbuild -setup”。然后用下面这行命令把myadd2函数编译成一个C++接口的DLL:

[Copy to clipboard] [ - ]

CODE:

mcc -W cpplib:libmyadd2 -T link:lib myadd2

结果,我们将会得到包含libmyadd2.dll,libmyadd2.ctf,libmyadd2.h,libmyadd2.lib等在内的一些文件。接下来我们只需要这四个文件。请注意:在MATLAB R2007b之前,这些文件都会在和编译

的m代码所在的目录中,而在MATLAB R2007b,缺省情况下这些文件会在Documents and Settings中用户的My Documents目录中。

此时,打开libmyadd2.h看看,在文件的最下面我们可以发现C++接口的函数定义。仔细观察过后,我们可以发现,这个接口函数的参数是按照这样的顺序定义的:输出参数的个数、输出参数、以及输入参数。

然后在VS2005中创建一个Win32 Console的VC++工程,我在测试时取名为testmyadd2_r2007b。把以上四个文件拷贝到VC++工程的源代码所在目录。

接下来设置VC++,让它能找到MATLAB接口函数的定义及连接库函数。可以有两种设置方式:一种是改VS2005中关于VC++的设置,这样的好处是每个新的工程都能自动获得这个设定;而另一种是只改当前工程的设置,也就是设置只对该工程有效。这里用后一种方式。

在VS2005中打开工程testmyadd2_r2007b,选择菜单“Project-->Properties,在出来的对话框上,把MATLAB提供的include路径加到VC++的头文件搜索路径。如图所示:

 

 vs2005_matlabr2007b_inc01.gif (25.26 KB)

 

然后把相应的lib所在目录加到linker的额外搜索路径上。如图所示:
接下来,告诉VC++,我们的这个程序需要连接到另外两个额外的库函数:libmyadd2.lib和mclmcrrt.lib。中间用空格隔开。如图所示:
最后则是程序代码。这个程序只有一个main函数,其完整代码附在下面给大家参考。欢迎大家拍砖。谢谢!

CODE:
#include "stdafx.h"

#include 
#include "mclmcr.h"
#include "mclcppclass.h"
#include "libmyadd2.h"


int _tmain(int argc, _TCHAR* argv[])
{

       std::cout << "Hello world!" << std::endl;
       /* Initialize the MCR */ 
       if( !mclInitializeApplication(NULL,0) ) 
       { 
            std::cout << "Could not initialize the application!" << std::endl;
            return -1; 
       } 

       // initialize lib
       if( !libmyadd2Initialize())
       {
            std::cout << "Could not initialize libmyadd2!" << std::endl;
            return -1; 
       }

       try
       {
       // declare and initialize a
       mwArray a(2, 2,   mxDOUBLE_CLASS);
       double *aData;
       aData = new double[4];
       int i;
       for( i=0; i<4; ++i)
       {
            aData[i] = 1.0*i;
       }
       // print output
       std::cout << "a = " << std::endl;
       std::cout << aData[0] << ", " << aData[1] << std::endl;
       std::cout << aData[2] << ", " << aData[3] << std::endl;

       a.SetData(aData, 4);

       // declare and initialize b
       mwArray b(2, 2,   mxDOUBLE_CLASS);
       b(1,1) = 11.;
       b(1,2) = 12.;
       b(2,1) = 21.;
       b(2,2) = 22.;

       mwArray y(2, 2,   mxDOUBLE_CLASS);
       mwArray z(2, 2,   mxDOUBLE_CLASS);

       // call the function
            myadd2(2, y, z, a, b);

       // copy data from mwArray to C++ objects

       // allocate outputs
       double *yData, *zData;
       yData = new double[4];
       if( yData == NULL )
       {
            std::cout << "Failed to allocate memory for yData!" << std::endl;
            return -1;
       }

       zData = new double[4];
       if( zData == NULL )
       {
            std::cout << "Failed to allocate memory for zData!" << std::endl;
            return -1;
       }

       // copy data from mwArray to C++
       y.GetData(yData, 4);
       z.GetData(zData, 4);

       // print output
       std::cout << "y = " << std::endl;
       std::cout << yData[0] << ", " << yData[1] << std::endl;
       std::cout << yData[2] << ", " << yData[3] << std::endl;

       std::cout << "z = " << std::endl;
       std::cout << zData[0] << ", " << zData[1] << std::endl;
       std::cout << zData[2] << ", " << zData[3] << std::endl;

      
       // deallocate memory
       delete [] aData;
       delete [] zData;
       delete [] yData;
       }
       catch( const mwException& e)
       {
      std::cerr << e.what() << std::endl;
               
       }
       // terminate the lib
       libmyadd2Terminate();

       // terminate MCR
       mclTerminateApplication();


       return 0;
}
 
2007-10-6 23:18
 vs2005_matlabr2007b_result.gif (9.26 KB) 

点击在新窗口查看全图
 

实验的结果表明,在C++程序中使用MATLAB编译产生的C++接口DLL非常容易。 mwArray也很容易使用。另外,最新的MATLAB R2007b中关于这部分并没有什么大的变化。这个练习适合MATLAB 7.0以后直到MATLAB R2007b的所有版本。

ask:

就我个人6.5混编的经验,我很少弄DLL,直接编译M文件为cpp,h文件即可,VC工程中引用,按.h文件格式调用即可。

这样混编基本没有任何难度,将MATLAB程序看成MFC一个类即可,看看extern里面

相关头文件的定义,跟看MSDN一样。

抛开mclTerminateApplication();这些,这应该是一个通用的办法

手头只有6.5,不知道7.5还能生成cpp么?

answer:
从matlab7.0开始,其编译器的功能就和以前版本不一样了。它不再把m代码翻译成C/C++源代码,而是对m代码作个简单的 wrapper,通常以动态链接库的形式提供给用户以方便用户在其他的程序中使用。所以,在matlab 7.x,编译m代码不能得到以前那种意义的C/C++源程序。

不过,仅从软件开发的角度来看,我觉得用DLL来打包更为方便。如果m函数的接口不变,那么每次改变m代码后,我们只需重新编译m代码,获得新的DLL及CTF,而不需要重新编译VC客户程序。这在开发大型软件时有很大的优越性。

多谢taohe的文章,我想问一下,ctf文件是干嘛用的,谢谢,记得matlab 6.5版里面编译为DLL时没这个东西啊。

这是matlab7.0以后matlab编译器的新特性。它和旧版技术不兼容。ctf其实就是一个zip文件,其中包含加密或者说编码过的一些m代码。其中有你自己的代码,以及matlab编译器通过分析你的代码根据依赖关系找到的其他一些m代码。

posted on 2012-02-16 14:47  知识天地  阅读(1381)  评论(0编辑  收藏  举报