最近接到一个工程项目,需要把一篇论文中的matlab代码转成C++链接库,中间出现了各种各样的问题…由于疫情宅家,闲来无事写一篇博客帮助有需要的人少走弯路,第一次写博客,存在问题欢迎指正。
我用的是Matlab 2018b和Visual Studio Community 2019,所有编译过程都是64位,这里用一个简单的demo举例子。
生成C++链接库
一个简单的矩阵相乘函数foo.m
function c = foo(a, b)
c = a * b;
end
使用mcc命令将其编译成C++库,需安装matlab compiler,且需要配置Visual C++编译器,亲测MinGw64是不行的(后面在VS的环境下会出问题),而且matlab 2018b无法匹配VC++2019,我的做法比较笨,但是不繁琐,就是又下载安装了一个VS2015,然后让matlab编译器设置为自带的VC++2015(使用mex -setup,具体可以参考其他博文)。
设置好这些之后,在matlab命令行中输入:
>> mcc -W 'cpplib:foo' foo.m
得到以下文件:
如果要转换的m文件较多,用命令行的方法比较麻烦,也可以使用matlab自带的图形界面,操作方法也是傻瓜式的,选好m文件后点确定就好了,但要注意每个m文件只能有1个函数(maybe)。
配置VS环境
我们只需要foo.h, foo.lib 和 foo.dll 这三个文件,将他们拷贝到VS的工程文件夹中,并将其路径添加到包含目录和库目录中。
matlab的矩阵结构对应C++的mwArray类,所以肯定要添加matlab的一些底层库,具体做法是:
包含目录中添加
D:\Program Files\MATLAB\R2018b\extern\include
库目录中添加
D:\Program Files\MATLAB\R2018b\extern\lib\win64\microsoft
(记得把matlab路径改成自己的)
环境配置还没完成,但先到一遍,我们写一个测试用的主程序test.cpp,其中foo函数的接口可以在foo.h中找,具体用法可以参考其他博文。
#pragma comment(lib, "foo.lib")
#pragma comment(lib, "mclmcr.lib")
#pragma comment(lib, "mclmcrrt.lib")
//链接所需的库,相当于在配置中添加附加依赖项
#include <iostream>
#include "foo.h"
using namespace std;
int main()
{
mclmcrInitialize();
if (!mclInitializeApplication(NULL, 0)) {
cout << "error1" << endl;
return -1;
}
// 上面两句初始化可以鉴定Matlab外部调用环境设置是否正确.
if (!fooInitialize()) {
cout << "error2" << endl;
return -1;
}
double data[4] = { 2.0, 3.0 };
mwArray m(1, 1, mxDOUBLE_CLASS);
m.SetData(data, 1);
mwArray n(1, 1, mxDOUBLE_CLASS);
n.SetData(data + 1, 1);
mwArray result(1, 1, mxDOUBLE_CLASS);
foo(1, result, m, n); //计算2*3
//测试的目标
cout << "result:" << result << endl;
fooTerminate();
mclTerminateApplication();
return 0;
}
此时可以顺利生成解决方案,如果出现了如下图的无法解析外部符号的error,那肯定是库没配好,找不到路径或者没有将某个必需的库写入依赖项。
接下来就是运行了,当然还不能成功,运行时会报错:
运行的目的是让它生成x64文件夹,只要把foo.dll放入.\x64\Debug或者.\x64\Release就解决这个问题了。
但有些人接下来可能还会碰到最令人头疼的内存冲突问题:
仔细观察发现VS会去matlab的文件夹加载巨多dll文件,然后自己把自己搞错(VS管得太宽)。查找相关论坛发现有人提出了一种解决方案,如下图把调试器类型改为仅限托管。
最后再运行一遍就能出正确结果辣, 开心~
对于多任务多函数的混编也是大同小异,都是一个套路。
希望看到这里的人不用为一些莫名其妙的error折腾好几天/(ㄒoㄒ)/~~