MATLAB与C++混合编程:动态链接库方法实现混合编程及常见错误解决办法

C++动态链接库方式调用MATLAB写好的函数,将C++的参数传递给MATLAB的dll,然后将结果返回给C++进行显示或处理。


编写MATLAB函数生成dll

function [y]=sincplot(n)
x1=0.01:0.01:n*pi;
y1=sin(x1)./x1;
x2=-n*pi:0.01:-0.01;
y2=sin(x2)./x2;
y=[y2 y1];
plot([x2 x1], y);
然后用MATLAB语句进行编译。

编译:mcc -B cpplib:libsincplot sincplot.m

注意由于我用的是C++,故应使用参数cpplib,若是C应该是使用csharedlib(应该是)。用错了,就会出现链接错误,这个bug卡了我一星期。详见混合编程MATLAB引擎的一些错误(error LNK2019: 无法解析的外部符号)

最后会生成很多文件,此处要用到的文件有三个:libsincplot.h,libsincplot.lib,libsincplot.dll。

C++环境的配置

新建C++工程,然后有几个需要配置的地方
  1. 项目->属性->配置属性->C++目录,将包含目录添加以下目录:F:\MATLAB R2010b\extern\include。就是MATLAB下的extern文件夹下去找。
  2. 项目->属性->配置属性->C++目录,将库目录添加以下目录:F:\MATLAB R2010b\extern\lib\win64\microsoft。由于我的MATLAB是64位的,应用win64文件夹下的文件。(我测试了一下,这个貌似不设置也是可以的)
  3. 项目->属性->配置属性->链接器->输入,将附加依赖项添加以下lib文件:libsincplot.lib(刚编译生成的),mclmcrrt.lib(在F:\MATLAB R2010b\extern\lib\win64\microsoft\目录下)。

C++程序编写

  • 将刚刚生成的libsincplot.h添加到工程当中。
  • 在要使用dll的文件中添加#include "libsincplot.h",一会要用到里面的函数,如libsincplotInitialize();mlxSincplot(1, &pArrayOut, 1, &pArrayIn);libsincplotInitialize();
  • 好了,编写我们的C++程序,我写了一个最简单的MFC程序,添加了一个菜单按扭,其响应函数如下:
void Ctestsincplot_dll1View::OnPlotsinc()
{
	// TODO: 在此添加命令处理程序代码
	//创建输入输出矩阵
	if(pArrayIn == NULL)
	{
		pArrayIn = mxCreateDoubleMatrix(1, 1, mxREAL);
		ASSERT(pArrayIn != NULL);
	}
	if (pArrayOut == NULL)
	{
		pArrayOut = mxCreateDoubleMatrix(1, 100, mxREAL);
		ASSERT(pArrayOut != NULL);
	}

	//初始化输入
	double* pDouble = new double(10 * (0.3 + 0.7 * rand() * 1.0 / RAND_MAX));
	memcpy((void*)mxGetPr(pArrayIn), (void*)pDouble, sizeof(pDouble));
	delete pDouble;

	//MATLAB函数
	mlxSincplot(1, &pArrayOut, 1, &pArrayIn);
	

	//调用输出显示于界面
	CDC* pDC = GetDC();
	int i = 0;
	int j = 0, k = 0;
	CString testStr;
	for (i = 0; i < mxGetNumberOfElements(pArrayOut); i++)
	{
		testStr.Format(_T("%6.2f"), *(mxGetPr(pArrayOut) + i));
		pDC->TextOutW(5 + j * 40, 5 + k * 15, testStr);
		j++;
		if(j > 20)
		{
			j = 0;
			k ++;
		}
	}
}
其中pArrayIn和pArrayOut都是在其头文件中定义的mxArray* 类型。
  • 在构造函数中添加libsincplotInitialize();在析构函数中添加libsincplotInitialize();。否则会导置你的mxCreateDoubleMatrix()等函数无效,一直失败。
  • 记得使用了mxCreateDoubleMatrix()新建了内存,就要配套使用mxDestroyArray()来消灭内存。
编译链接生成
先编译一下,生成Debug文件夹,然后会出错显示缺少dll文件,此时把生成的libsincplot.dll拷贝到Debug文件夹下
由于我的MATLAB是64位的,所以编译时应修改解决方案平台,把win32改成x64,否则会出现错误:error LNK2019: 无法解析的外部符号

总结

如上,程序应该可以完整执行了,其实都是小问题,却卡了我近两周时间。始终记住编译环境是C++,始终记住无论MATLAB生成dll还是VS生成exe,全是64位(取决于你的MATLAB了),就可以减少网上很多放了几百年都没人回答的问题。
记住 在构造函数中添加 libsincplotInitialize();在析构函数中添加libsincplotInitialize();才能够使得MATLAB生成的dll可用。

参考书目:《精通Matlab与C/C++混合程序设计》第三版刘维编著。原照着书上做的几个错误全在上面有提到了,可能是因为源程序是为C而不是为C++写的。
程序最后的效果图是:
菜单按钮
MATLAB与C++混合编程:动态链接库方法实现混合编程及常见错误解决办法_第1张图片
点击以后效果
MATLAB与C++混合编程:动态链接库方法实现混合编程及常见错误解决办法_第2张图片
以下是View.h和View.cpp源程序:
// testsincplot_dll1View.h : Ctestsincplot_dll1View 类的接口
//

#pragma once
#include "F:\MATLAB R2010b\MyWorks\DLL\libsincplot.h"


class Ctestsincplot_dll1View : public CView
{
protected: // 仅从序列化创建
	Ctestsincplot_dll1View();
	DECLARE_DYNCREATE(Ctestsincplot_dll1View)

// 特性
public:
	Ctestsincplot_dll1Doc* GetDocument() const;

// 操作
public:

// 重写
public:
	virtual void OnDraw(CDC* pDC);  // 重写以绘制该视图
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:

// 实现
public:
	virtual ~Ctestsincplot_dll1View();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// 生成的消息映射函数
protected:
	afx_msg void OnFilePrintPreview();
	afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnPlotsinc();

public:
	mxArray* pArrayIn;
	mxArray* pArrayOut;
};

#ifndef _DEBUG  // testsincplot_dll1View.cpp 中的调试版本
inline Ctestsincplot_dll1Doc* Ctestsincplot_dll1View::GetDocument() const
   { return reinterpret_cast(m_pDocument); }
#endif
// testsincplot_dll1View.cpp : Ctestsincplot_dll1View 类的实现
//

#include "stdafx.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "testsincplot_dll1.h"
#endif

#include "testsincplot_dll1Doc.h"
#include "testsincplot_dll1View.h"
#include "F:\MATLAB R2010b\extern\include\matrix.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Ctestsincplot_dll1View

IMPLEMENT_DYNCREATE(Ctestsincplot_dll1View, CView)

BEGIN_MESSAGE_MAP(Ctestsincplot_dll1View, CView)
	ON_WM_CONTEXTMENU()
	ON_WM_RBUTTONUP()
	ON_COMMAND(ID_PLOTSINC, &Ctestsincplot_dll1View::OnPlotsinc)
END_MESSAGE_MAP()

// Ctestsincplot_dll1View 构造/析构

Ctestsincplot_dll1View::Ctestsincplot_dll1View()
{
	// TODO: 在此处添加构造代码
	pArrayIn = NULL;
	pArrayOut = NULL;
	libsincplotInitialize();
	
}

Ctestsincplot_dll1View::~Ctestsincplot_dll1View()
{
	if (pArrayIn != NULL)
	{
		mxDestroyArray(pArrayIn);
	}
	if (pArrayOut != NULL)
	{
		mxDestroyArray(pArrayOut);
	}
	libsincplotTerminate();
}

BOOL Ctestsincplot_dll1View::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: 在此处通过修改
	//  CREATESTRUCT cs 来修改窗口类或样式

	return CView::PreCreateWindow(cs);
}

// Ctestsincplot_dll1View 绘制

void Ctestsincplot_dll1View::OnDraw(CDC* /*pDC*/)
{
	Ctestsincplot_dll1Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
}

void Ctestsincplot_dll1View::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
	ClientToScreen(&point);
	OnContextMenu(this, point);
}

void Ctestsincplot_dll1View::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
	theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// Ctestsincplot_dll1View 诊断

#ifdef _DEBUG
void Ctestsincplot_dll1View::AssertValid() const
{
	CView::AssertValid();
}

void Ctestsincplot_dll1View::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

Ctestsincplot_dll1Doc* Ctestsincplot_dll1View::GetDocument() const // 非调试版本是内联的
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(Ctestsincplot_dll1Doc)));
	return (Ctestsincplot_dll1Doc*)m_pDocument;
}
#endif //_DEBUG


// Ctestsincplot_dll1View 消息处理程序


void Ctestsincplot_dll1View::OnPlotsinc()
{
	// TODO: 在此添加命令处理程序代码
	//创建输入输出矩阵
	if(pArrayIn == NULL)
	{
		pArrayIn = mxCreateDoubleMatrix(1, 1, mxREAL);
		ASSERT(pArrayIn != NULL);
	}
	if (pArrayOut == NULL)
	{
		pArrayOut = mxCreateDoubleMatrix(1, 100, mxREAL);
		ASSERT(pArrayOut != NULL);
	}

	//初始化输入
	double* pDouble = new double(10 * (0.3 + 0.7 * rand() * 1.0 / RAND_MAX));
	memcpy((void*)mxGetPr(pArrayIn), (void*)pDouble, sizeof(pDouble));
	delete pDouble;

	//MATLAB函数
	mlxSincplot(1, &pArrayOut, 1, &pArrayIn);
	

	//调用输出显示于界面
	CDC* pDC = GetDC();
	int i = 0;
	int j = 0, k = 0;
	CString testStr;
	for (i = 0; i < mxGetNumberOfElements(pArrayOut); i++)
	{
		testStr.Format(_T("%6.2f"), *(mxGetPr(pArrayOut) + i));
		pDC->TextOutW(5 + j * 40, 5 + k * 15, testStr);
		j++;
		if(j > 20)
		{
			j = 0;
			k ++;
		}
	}
}







你可能感兴趣的:(C++笔记,C++MATLAB混合编程,MFC编程,C++错误)