matlab调用C的动态库

发布时间 2023-12-26 15:53:40作者: yangzifb

在使用matlab进行算法开发时,实现代码往往是C/C++的。而算法实现后,又需要通过matlab进行数据分析,对算法进行验证。

此时若使用matlab代码对数据进行分析,则会面临matlab实现与C代码实现不一致的情况。所以需要通过matlab调用c代码,实现历史数据回访验证。

使用matlab调用c代码的方法有几种,其中最方便的就是通过调用c动态库的方式。

网上的教程往往没有讲matlab的配置,调用前matlab需要配置编译器:

matlab需要配置mingw64编译器,可能需要版本对应。2016a对应gcc 6.2是没问题的。对应其他版本没有试过

setenv('MW_MINGW64_LOC','C:\software\cmder\msys64\mingw64');

然后输入: mex -setup 进行配置

然后可以根据提示,选择 mex -setup C++ 

C/C++部分代码需要编译为动态库,需要matlab调用的函数要通过extern "C"声明为C的命名规则。可传入结构体指针以及基础数据结构。函数可通过返回值和指针输出数据。

c/c++部分提供动态库dll或so,和h文件共2个文件。

通过matlab调用时,可通过头文件导入C部分的结构体。从而实现数据结构的传递。

测试C代码:(main.cpp)

 1 #include "common.h"
 2 #include "test.h"
 3 
 4 int add(double a,float b,int c,u16 d,s8 e)
 5 {
 6     return a+b+c+d+e;
 7 }
 8 int struct_test(S_PRO_IN *pin) //测试结构体指针的输入
 9 {
10     printf("val_u8 %d\n",pin->val_u8); //在matlab调用中无效
11     pin->val_u8++; //测试基础数据类型
12     pin->val_s8++; //测试基础数据类型
13     pin->val_u16++; //测试基础数据类型
14     pin->val_s16++; //测试基础数据类型
15     pin->val_u32++; //测试基础数据类型
16     pin->val_s32++; //测试基础数据类型
17     pin->val_u64++; //测试基础数据类型
18     pin->val_s64++; //测试基础数据类型
19     pin->val_float=9.2;
20     pin->val_double=-9.2;
21     pin->val_u32p[2]=10; //测试整数指针
22     return 2;
23 }
24 #ifndef SOMAKE //若是编译成exe
25 int main(int argc, char *argv[])
26 {
27     int i;
28     printf("add: %d\n",add(1,2,3,4,-1));
29     PDBG;
30     scanf("%c",(char)i);
31 }
32 #endif

头文件(test.h)

 1 #ifndef TEST_H
 2 #define TEST_H
 3 
 4 #include "main.h"
 5 
 6 //matlab调用dll时:
 7 //    可定义结构体,一次传输
 8 //    通过头文件进行接口交互
 9 //
10 
11 typedef struct S_PRO_IN_ //matlab中的内存布局是否一致?
12 {
13     u8 val_u8; //测试基础数据类型
14     s8 val_s8; //测试基础数据类型
15     u16 val_u16; //测试基础数据类型
16     s16 val_s16; //测试基础数据类型
17     u32 val_u32; //测试基础数据类型
18     s32 val_s32; //测试基础数据类型
19     u64 val_u64; //测试基础数据类型
20     s64 val_s64; //测试基础数据类型
21     float val_float;
22     double val_double;
23     u32 *val_u32p; //测试整数指针
24 } S_PRO_IN; //
25 
26 #ifdef __cplusplus
27 extern "C"
28 {
29 #endif
30 
31 int add(double a,float b,int c,u16 d,s8 e);
32 int struct_test(S_PRO_IN *pin); //测试结构体指针的输入
33 
34 #ifdef __cplusplus
35 }
36 #endif
37 
38 #endif

文件中,定义了2个接口函数,add和struct_test用于测试调用的传参和返回情况。

在24行定义了条件编译测试函数,用于使用exe对动态库的函数进行测试,并找到此编译器的依赖dll

代码编译成test.exe后,在Windows资源管理器中双击运行,会报错:

 根据提示,在mingw64路径下将所缺失的dll复制到exe同目录下即可。

然后编译为动态库.so。编译选项加入: -D SOMAKE -fPIC,链接选项加入: -shared,编译为test.so

测试matlab代码:

if not (libisloaded('test.so'))
	[m1,m2] = loadlibrary('test.so','test.h');
	disp(m1);
	disp(m2); %显示变量内容
end

libfunctions('test','-full'); %查看加载的函数列表

%构造指针
val_u32p=[1,2,3,4.8,-5];
%构造结构
pro_in.val_u8=3;
pro_in.val_float=-2.1;
pro_in.val_u32p=libpointer('uint32Ptr',val_u32p);
%pro_in_struct=libstruct('S_PRO_IN_',pro_in);

%调用函数
a=calllib('test','add',1,2,3,4,-1);
[b,c]=calllib('test','struct_test',pro_in); %函数返回值为b,c为指针对象
%注意,指针输入以后,输入对象是不变的,返回的c是输入指针变化后的对象

 使用loadlibrary加载动态库,使用unloadlibrary卸载动态库。卸载了动态库,才能更新动态库。卸载前必须将所有用到的对象都清除掉。

unloadlibrary('test'); %注意这里不要写扩展名,加载的时候不是dll,需要写扩展名

   通过实验,得出数据传递过程中的几个结论:

  1. 构造数组时,可通过matlab中的double型数组,构造其他类型的数组,类型会自动转换,正负号、小数部分都会自动转换(小数部分是舍入)。
  2. 构造结构时,可不对所有域赋值
  3. 函数的参数如果是指针,指针所指的空间不会改变,函数内部对指针数据的改变会被复制一遍,放在返回列表中
  4. 调用函数时,返回值是一个数组,C函数的返回值放在第一位,函数中传入的参数如果是指针,会被复制一份,依次放在序列中。