正确使用Matlab的Library Compiler
概述
MATLAB Library Compiler 是 MATLAB 的一个强大工具,它可以将 MATLAB 函数编译成动态链接库(DLL),使得其他编程语言(如 C/C++)可以调用 MATLAB 函数。这为跨语言集成提供了便利,特别适用于需要在高性能应用中集成 MATLAB 算法的场景。
前提条件
在目标计算机上运行编译后的 DLL 之前,必须安装相应版本的 MCR。MCR 是一个独立的运行时环境,不需要完整的 MATLAB 安装。实际上,采用Matla编译到C或者C++的动态链接库文件时,提供会提供MCR的安装包,只需要安装即可。由两种形式,网络安装或者离线安装。
前面一段时间在知乎开玩笑:用Matlab写东西,一句代码也可以跟你搞上一张DVD,简直是诚意满满。
- 编译器: Visual Studio (Windows) 或 GCC (Linux)
- CMake: 用于构建系统管理
- MATLAB: 用于编译 MATLAB 函数为 DLL
步骤
第1步:准备 MATLAB 函数
首先创建要编译的 MATLAB 函数。以下是一个简单的示例:
1function r = easy(a,b)
2r = a + b;
3end
第2步:使用 Library Compiler 编译
在 MATLAB 命令窗口中执行以下命令:
1libraryCompiler
至于这玩意怎么用,请不要来问我,我也不知道。编译成功后会生成以下文件:
easy.dll
- 动态链接库easy.lib
- 链接库easy.h
- C语言头文件
编译器会自动生成 C 语言接口的头文件,按照这个接口文件,就可以使用函数easy
,只不过名字变成了mlfEasy
。这里的mlf
大概是Matlab Live Forver
的缩写,当然也可能是Matlab Love Fucking
的缩写。
1/*
2 * MATLAB Compiler: 23.2 (R2023b)
3 * Date: Sat May 24 09:12:22 2025
4 * Arguments:
5 * "-B""macro_default""-W""lib:easy,version=1.0""-T""link:lib""-d""D:\writing\qc
6 * hen-fdii-cardc.github.io\static\matlab\easy_dll\easy\for_testing""-v""D:\writ
7 * ing\qchen-fdii-cardc.github.io\static\matlab\easy_dll\easy.m"
8 */
9
10#ifndef easy_h
11#define easy_h 1
12
13#if defined(__cplusplus) && !defined(mclmcrrt_h) && defined(__linux__)
14# pragma implementation "mclmcrrt.h"
15#endif
16#include "mclmcrrt.h"
17#ifdef __cplusplus
18extern "C" { // sbcheck:ok:extern_c
19#endif
20
21/* This symbol is defined in shared libraries. Define it here
22 * (to nothing) in case this isn't a shared library.
23 */
24#ifndef LIB_easy_C_API
25#define LIB_easy_C_API /* No special import/export declaration */
26#endif
27
28/* GENERAL LIBRARY FUNCTIONS -- START */
29
30extern LIB_easy_C_API
31bool MW_CALL_CONV easyInitializeWithHandlers(
32 mclOutputHandlerFcn error_handler,
33 mclOutputHandlerFcn print_handler);
34
35extern LIB_easy_C_API
36bool MW_CALL_CONV easyInitialize(void);
37extern LIB_easy_C_API
38void MW_CALL_CONV easyTerminate(void);
39
40extern LIB_easy_C_API
41void MW_CALL_CONV easyPrintStackTrace(void);
42
43/* GENERAL LIBRARY FUNCTIONS -- END */
44
45/* C INTERFACE -- MLX WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- START */
46
47extern LIB_easy_C_API
48bool MW_CALL_CONV mlxEasy(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]);
49
50/* C INTERFACE -- MLX WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- END */
51
52/* C INTERFACE -- MLF WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- START */
53
54extern LIB_easy_C_API bool MW_CALL_CONV mlfEasy(int nargout, mxArray** r, mxArray* a, mxArray* b);
55
56#ifdef __cplusplus
57}
58#endif
59/* C INTERFACE -- MLF WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- END */
60
61#endif
C 语言集成
以下是调用 MATLAB DLL 的完整 C 程序示例:
1#include <stdio.h>
2#include "easy.h"
3
4#ifdef _WIN32
5#include <windows.h>
6#include <process.h>
7#endif
8
9// Correct function signatures with proper calling convention
10static int errorHandler(const char *msg)
11{
12 printf("MATLAB Error: %s\n", msg);
13 fflush(stdout);
14 return 0;
15}
16
17static int printHandler(const char *msg)
18{
19 printf("MATLAB Message: %s\n", msg);
20 fflush(stdout);
21 return 0;
22}
23
24int main()
25{
26 printf("Starting MATLAB runtime initialization...\n");
27 fflush(stdout);
28
29 const char *args[] = {"-singleCompThread"};
30 if (!mclInitializeApplication(args, 1))
31 {
32 printf("Failed to initialize MATLAB runtime\n");
33 fflush(stdout);
34 return -1;
35 }
36
37 // Initialize MATLAB runtime with handlers
38 if (!easyInitializeWithHandlers(errorHandler, printHandler))
39 {
40 printf("Failed to initialize MATLAB runtime\n");
41 fflush(stdout);
42 return -1;
43 }
44 printf("MATLAB runtime initialized successfully\n");
45 fflush(stdout);
46
47 // Create input parameters
48 mxArray *a = mxCreateDoubleScalar(1.0);
49 mxArray *b = mxCreateDoubleScalar(2.0);
50 mxArray *result = NULL;
51
52 // Call MATLAB function
53 if (mlfEasy(1, &result, a, b))
54 {
55 // Get result
56 double *resultData = mxGetPr(result);
57 printf("Calculation result: %f\n", resultData[0]);
58 fflush(stdout);
59
60 // Free result array
61 mxDestroyArray(result);
62 }
63 else
64 {
65 printf("Function call failed\n");
66 fflush(stdout);
67 }
68
69 // Clean up input parameters
70 mxDestroyArray(a);
71 mxDestroyArray(b);
72
73 printf("Terminating MATLAB runtime...\n");
74 fflush(stdout);
75
76 // Terminate MATLAB runtime
77 easyTerminate();
78
79 printf("easy library runtime terminated\n");
80 fflush(stdout);
81
82 printf("Attempting graceful exit...\n");
83 fflush(stdout);
84
85 if (!mclTerminateApplication())
86 {
87 printf("Failed to terminate MATLAB runtime\n");
88 fflush(stdout);
89
90 // Try multiple termination methods
91 printf("Using TerminateProcess...\n");
92 fflush(stdout);
93
94#ifdef _WIN32
95 // Use TerminateProcess instead of ExitProcess
96 HANDLE hProcess = GetCurrentProcess();
97 TerminateProcess(hProcess, -1);
98#endif
99 return -1;
100 }
101 // If we reach here, TerminateProcess failed
102 printf("mclTerminateApplication succeeded, exit...\n");
103 fflush(stdout);
104 return 0;
105}
关键的步骤和代码:
- 使用
mxCreateDoubleScalar
创建输入参数 - 调用
mlfEasy
执行 MATLAB 函数 - 使用
mxGetPr
获取结果数据
构建配置
CMakeLists.txt 配置
1cmake_minimum_required(VERSION 3.10)
2project(matlab_dll_demo C)
3
4# 设置C标准
5set(CMAKE_C_STANDARD 11)
6set(CMAKE_C_STANDARD_REQUIRED ON)
7
8# 设置MATLAB安装路径
9set(MATLAB_ROOT "C:/Program Files/MATLAB/R2023b")
10set(MATLAB_INCLUDE_DIRS
11 "${MATLAB_ROOT}/extern/include"
12 "${MATLAB_ROOT}/sys/opengl/include"
13 "${MATLAB_ROOT}/sys/os/glnxa64"
14)
15set(MATLAB_LIBRARY_DIRS "${MATLAB_ROOT}/extern/lib/win64/microsoft")
16
17# 设置Release版本的编译选项
18if(MSVC)
19 set(CMAKE_C_FLAGS_RELEASE "/O2 /DNDEBUG")
20else()
21 set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
22endif()
23
24# 添加可执行文件
25add_executable(${PROJECT_NAME} main.c easy.lib)
26
27# 添加头文件目录
28target_include_directories(${PROJECT_NAME} PRIVATE
29 ${CMAKE_CURRENT_SOURCE_DIR}
30 ${MATLAB_INCLUDE_DIRS}
31)
32
33# 添加库文件目录
34link_directories(${MATLAB_LIBRARY_DIRS})
35
36# 链接MATLAB DLL和运行时库
37target_link_libraries(${PROJECT_NAME} PRIVATE
38 ${CMAKE_CURRENT_SOURCE_DIR}/easy.lib
39 "${MATLAB_LIBRARY_DIRS}/libmex.lib"
40 "${MATLAB_LIBRARY_DIRS}/libmx.lib"
41 "${MATLAB_LIBRARY_DIRS}/libmat.lib"
42 "${MATLAB_LIBRARY_DIRS}/mclmcrrt.lib"
43)
44
45# 复制DLL到输出目录
46add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
47 COMMAND ${CMAKE_COMMAND} -E copy_if_different
48 ${CMAKE_CURRENT_SOURCE_DIR}/easy.dll
49 $<TARGET_FILE_DIR:${PROJECT_NAME}>)
唯一需要注意的就是替换Matlab的安装路径。然后就是一气呵成的
1cmake -B build
2cmake --build build --config Release
3build\Release\matlab_dll_demo.exe
完美。
员工通道
实际上,我是故意要把什么CMake写在前面凑字数。Matlab中提供非常简单的工具产生exe文件。
1mbuild main.c easy.lib
这是windows下,linux下是
1mbuild main.c -L. -leasy
当然这两个前提都是把easy.lib/easy.dll和easy.h放在当前目录下。这里面Linux那个-L.
就是告诉编译器,动态链接库在当前目录下。
我就不这么办,我既要让AI帮我写一个CMakeLists.txt。
一个小坑
当然,在使用Matlab编译的DLL时,会有两个小坑,说是1个自然就是2个,四大猛男当然是5个人。
- 一个问题就是必须配对使用两个函数:
mclInitializeApplication
mclTerminateApplication
- 另外一个问题就是如果在Matlab代码中使用了多线程或者类似玩意,可能会有坑。所以在上面那个函数调用是可以加上
-singleCompThread
参数。
这个问题在undocumentedmatlab.com有详细说明。
实际上,我作为一个狠人,我还准备了Windows下面的狠活。
1// 使用强制终止
2#ifdef _WIN32
3HANDLE hProcess = GetCurrentProcess();
4TerminateProcess(hProcess, 0);
5#endif
总结
MATLAB Library Compiler 为 MATLAB 算法的跨语言集成提供了强大的解决方案。
不要干什么把M文件翻译成dll,然后又在Matlab中用loadlibrary加载的事情。
那个功能是pcode,不是这个,下回再说吧。看过我以前帖子的朋友自然会help pcode
, doc pcode
一套连招。
文章标签
|-->matlab |-->cpp |-->library_compiler |-->mcr
- 本站总访问量:次
- 本站总访客数:人
- 可通过邮件联系作者:Email大福
- 也可以访问技术博客:大福是小强
- 也可以在知乎搞抽象:知乎-大福
- Comments, requests, and/or opinions go to: Github Repository