Cover Image

Line 7, Shanghai Metro

注意:MATLAB Coder不能适用于所有情况,且并非所有MATLAB函数都支持转换为C/C++代码
MATLAB Coder可以将MATLAB代码转换为可读并且可移植的C/C++代码。因此,在求解数学问题的时候,可以先编写较为简单的MATLAB代码,然后将其转换为C/C++代码在不同场景下使用。


使用MATLAB Coder生成代码的步骤如下:

  • 编写实现功能的MATLAB代码
  • 检查MATLAB代码的兼容性
  • 生成C/C++代码

一个例子

下面以一个实际问题为例,介绍代码的生成步骤。
例子很简单:生成一个$1000\times2$的随机数矩阵,随机数取值范围是$[-100000,100000]$,然后使用扩展欧几里得算法计算矩阵每行的两个数的最大公约数及其线性表示,并存储在一个$1000\times3$的矩阵中,将生成的随机数矩阵和计算结果输出到文件中。

准备代码

MATLAB功能模块代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
%solve.m
function solve
fprintf('Generating random matrix...\r\n');
src = randi([-100000 100000], 1000, 2);
file=fopen('result.txt','w');
fprintf(file,'The source is:\r\n');
fprintf(file,'\ta\tb\r\n');
for j=1:1000
fprintf(file,'\t%f\t%f\r\n',src(j,1),src(j,2));
end
res = zeros(1000,3);
fprintf('Calculating, please wait...\r\n');
for i=1:1000
[r,x,y]=gcd(src(i,1),src(i,2));
res(i,1)=r;
res(i,2)=x;
res(i,3)=y;
end
fprintf(file,'The result is:(r=gcd(a,b)=ax+by)\r\n');
fprintf(file,'\tr\tx\ty\r\n');
for k=1:1000
fprintf(file,'\t%f\t%f\t%f\r\n',res(k,1),res(k,2),res(k,3));
end
fprintf('Done.\r\n');
fclose(file);
end

对功能模块进行测试的代码如下所示:

1
2
%test.m
solve

在转换之前,需要对MATLAB代码进行如下检查:

  • 检查语法问题
  • 检查代码中是否包含不能被转换的函数
  • 检查编译及运行时问题

在使用MATLAB Coder生成代码之前,MATLAB Coder也会进行上述检查。
检查完毕后,保存MATLAB代码为一个.m文件。

使用MATLAB Coder生成代码

在MATLAB中的App选项卡里找到MATLAB Coder并打开。

选择保存有MATLAB代码的.m文件,点击Next。

然后确定测试代码的输入数据,可以直接指定也可以加载事先编写的测试文件。由于本例的代码中没有输入,因此跳过。

进行运行时测试,需要载入一个调用此函数的MATLAB代码文件。这个步骤是可选的。

生成代码,可以选择C或者C++,本例中选择的是C++,然后点击Generate,稍等片刻即可生成代码。


代码生成成功。

验证生成的代码

本例将在Visual Studio 2017中验证生成的代码。
使用MATLAB Coder生成的文件树状图如下所示:
solve
│ buildInfo.mat
│ codeInfo.mat
│ eml_rand.cpp
│ eml_rand.h
│ eml_rand_mcg16807_stateful.cpp
│ eml_rand_mcg16807_stateful.h
│ eml_rand_mt19937ar_stateful.cpp
│ eml_rand_mt19937ar_stateful.h
│ eml_rand_shr3cong_stateful.cpp
│ eml_rand_shr3cong_stateful.h
│ fclose.cpp
│ fclose.h
│ fileManager.cpp
│ fileManager.h
│ gcd.cpp
│ gcd.h
│ gcGuiReport.mat
│ rand.cpp
│ rand.h
│ randi.cpp
│ randi.h
│ rtGetInf.cpp
│ rtGetInf.h
│ rtGetNaN.cpp
│ rtGetNaN.h
│ rtwtypes.h
│ rtw_proj.tmw
│ rt_nonfinite.cpp
│ rt_nonfinite.h
│ setup_msvc150.bat
│ solve.cpp
│ solve.h
│ solve_data.cpp
│ solve_data.h
│ solve_initialize.cpp
│ solve_initialize.h
│ solve_ref.rsp
│ solve_rtw.bat
│ solve_rtw.mk
│ solve_rtw.rsp
│ solve_rtwutil.cpp
│ solve_rtwutil.h
│ solve_rtw_comp.rsp
│ solve_rtw_ref.rsp
│ solve_terminate.cpp
│ solve_terminate.h
│ solve_types.h
│ tree.txt

├─examples
│ main.cpp
│ main.h

├─html
│ report.mldatx

└─interface
_coder_solve_api.c
_coder_solve_api.h
_coder_solve_info.c
_coder_solve_info.h
_coder_solve_mex.cpp
_coder_solve_mex.h

在Visual Studio中新建一个空项目,然后将主目录与examples子目录下的.h与.cpp文件添加进项目,interface子目录下的文件不需要添加。

此外,还需要一个头文件tmwtypes.h,这个文件位于MATLAB安装目录的/extern/include文件夹内,添加进项目即可。
最后,由于在Visual Studio 2017的默认设置中,使用fopen之类的IO函数会因为不安全而无法通过编译,因此要在项目属性内关闭SDL检查。

然后生成可执行文件,运行之后可执行文件所在目录会出现存储随机数矩阵和运算结果的文本文件。

结语

这是我第一次使用MATLAB Coder生成代码。这个工具可以让把我从编码细节中解放出来,专注于功能实现,在编写出实现功能的MATLAB代码后,将其转换为C/C++语言,然后供后续使用。
不过,虽然用MATLAB生成了C++代码,但是代码量过大而且风格十分诡异,用来交作业或者交到OJ上基本是不可能的了。
而且,生成代码的性能也不一定是最优的。但总之,还是要好好学习,善于利用工具。

附:生成的C++代码

本例生成的文件较为繁多,以下仅贴出一部分关键的源代码文件。

main.cpp—程序入口函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* Include files */
#include "rt_nonfinite.h"
#include "solve.h"
#include "main.h"
#include "solve_terminate.h"
#include "solve_initialize.h"

/* Function Declarations */
static void main_solve();

/* Function Definitions */
static void main_solve()
{
/* Call the entry-point 'solve'. */
solve();
}

int main(int, const char * const [])
{
/* Initialize the application.
You do not need to do this more than one time. */
solve_initialize();

/* Invoke the entry-point functions.
You can call entry-point functions multiple times. */
main_solve();

/* Terminate the application.
You do not need to do this more than one time. */
solve_terminate();
return 0;
}

solve.cpp—功能模块实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/* Include files */
#include <string.h>
#include "rt_nonfinite.h"
#include "solve.h"
#include "fclose.h"
#include "fileManager.h"
#include "gcd.h"
#include "randi.h"
#include <stdio.h>

/* Function Definitions */
void solve()
{
double src[2000];
signed char fileid;
FILE * filestar;
boolean_T autoflush;
int j;
double res[3000];
printf("Generating random matrix...\r\n");
fflush(stdout);
randi(src);
fileid = cfopen("result.txt", "wb");
fileManager((double)fileid, &filestar, &autoflush);
if (!(filestar == NULL)) {
fprintf(filestar, "The source is:\r\n");
if (autoflush) {
fflush(filestar);
}
}

fileManager((double)fileid, &filestar, &autoflush);
if (!(filestar == NULL)) {
fprintf(filestar, "\ta\tb\r\n");
if (autoflush) {
fflush(filestar);
}
}

fileManager((double)fileid, &filestar, &autoflush);
for (j = 0; j < 1000; j++) {
if (!(filestar == NULL)) {
fprintf(filestar, "\t%f\t%f\r\n", src[j], src[1000 + j]);
if (autoflush) {
fflush(filestar);
}
}
}

memset(&res[0], 0, 3000U * sizeof(double));
printf("Calculating, please wait...\r\n");
fflush(stdout);
for (j = 0; j < 1000; j++) {
gcd(src[j], src[1000 + j], &res[j], &res[1000 + j], &res[2000 + j]);
}

fileManager((double)fileid, &filestar, &autoflush);
if (!(filestar == NULL)) {
fprintf(filestar, "The result is:(r=gcd(a,b)=ax+by)\r\n");
if (autoflush) {
fflush(filestar);
}
}

fileManager((double)fileid, &filestar, &autoflush);
if (!(filestar == NULL)) {
fprintf(filestar, "\tr\tx\ty\r\n");
if (autoflush) {
fflush(filestar);
}
}

fileManager((double)fileid, &filestar, &autoflush);
for (j = 0; j < 1000; j++) {
if (!(filestar == NULL)) {
fprintf(filestar, "\t%f\t%f\t%f\r\n", res[j], res[1000 + j], res[2000 + j]);
if (autoflush) {
fflush(filestar);
}
}
}

printf("Done.\r\n");
fflush(stdout);
b_fclose((double)fileid);
}

gcd.cpp—扩展欧几里得算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* Include files */
#include <cmath>
#include "rt_nonfinite.h"
#include "solve.h"
#include "gcd.h"

/* Function Definitions */
void gcd(double a, double b, double *g, double *c, double *d)
{
boolean_T p;
double x_tmp;
boolean_T guard1 = false;
double clast;
double dlast;
double q;
double r;
double b_a;
p = false;
x_tmp = std::floor(a);
if (rtIsInf(x_tmp) || rtIsNaN(x_tmp) || (a != x_tmp)) {
p = true;
}

guard1 = false;
if (p) {
guard1 = true;
} else {
p = false;
x_tmp = std::floor(b);
if (rtIsInf(x_tmp) || rtIsNaN(x_tmp) || (b != x_tmp)) {
p = true;
}

if (p) {
guard1 = true;
} else if (b == 0.0) {
*g = a;
*c = !(a == 0.0);
*d = 0.0;
} else {
*g = b;
clast = 1.0;
*c = 0.0;
dlast = 0.0;
*d = 1.0;
q = a / b;
if (q < 0.0) {
q = std::ceil(q);
} else {
q = std::floor(q);
}

for (r = a - b * q; r != 0.0; r = b_a - r * q) {
b_a = *g;
*g = r;
x_tmp = *c;
*c = clast - *c * q;
clast = x_tmp;
x_tmp = *d;
*d = dlast - *d * q;
dlast = x_tmp;
q = b_a / r;
if (q < 0.0) {
q = std::ceil(q);
} else {
q = std::floor(q);
}
}

if (*g < 0.0) {
*g = -*g;
*c = -*c;
*d = -*d;
}
}
}

if (guard1) {
*g = rtNaN;
*c = rtNaN;
*d = rtNaN;
}
}

/* End of code generation (gcd.cpp) */

rand.cpp—生成随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* Include files */
#include <string.h>
#include "rt_nonfinite.h"
#include "solve.h"
#include "rand.h"
#include "solve_data.h"

/* Variable Definitions */
static unsigned int c_state[625];

/* Function Declarations */
static double eml_rand_mt19937ar(unsigned int d_state[625]);

/* Function Definitions */
static double eml_rand_mt19937ar(unsigned int d_state[625])
{
double r;
int j;
unsigned int u[2];
unsigned int mti;
int kk;
unsigned int y;

do {
for (j = 0; j < 2; j++) {
mti = d_state[624] + 1U;
if (mti >= 625U) {
for (kk = 0; kk < 227; kk++) {
y = (d_state[kk] & 2147483648U) | (d_state[kk + 1] & 2147483647U);
if ((y & 1U) == 0U) {
y >>= 1U;
} else {
y = y >> 1U ^ 2567483615U;
}

d_state[kk] = d_state[kk + 397] ^ y;
}

for (kk = 0; kk < 396; kk++) {
y = (d_state[kk + 227] & 2147483648U) | (d_state[kk + 228] &
2147483647U);
if ((y & 1U) == 0U) {
y >>= 1U;
} else {
y = y >> 1U ^ 2567483615U;
}

d_state[kk + 227] = d_state[kk] ^ y;
}

y = (d_state[623] & 2147483648U) | (d_state[0] & 2147483647U);
if ((y & 1U) == 0U) {
y >>= 1U;
} else {
y = y >> 1U ^ 2567483615U;
}

d_state[623] = d_state[396] ^ y;
mti = 1U;
}

y = d_state[(int)mti - 1];
d_state[624] = mti;
y ^= y >> 11U;
y ^= y << 7U & 2636928640U;
y ^= y << 15U & 4022730752U;
u[j] = y ^ y >> 18U;
}

u[0] >>= 5U;
u[1] >>= 6U;
r = 1.1102230246251565E-16 * ((double)u[0] * 6.7108864E+7 + (double)u[1]);
} while (r == 0.0);

return r;
}

void b_rand(double r[2000])
{
int mti;
int hi;
unsigned int b_r;
unsigned int b;
if (method == 4U) {
for (mti = 0; mti < 2000; mti++) {
hi = (int)(state / 127773U);
b_r = 16807U * (state - hi * 127773U);
b = 2836U * hi;
if (b_r < b) {
state = ~(b - b_r) & 2147483647U;
} else {
state = b_r - b;
}

r[mti] = (double)state * 4.6566128752457969E-10;
}
} else if (method == 5U) {
for (mti = 0; mti < 2000; mti++) {
b_r = 69069U * b_state[0] + 1234567U;
b = b_state[1] ^ b_state[1] << 13;
b ^= b >> 17;
b ^= b << 5;
b_state[0] = b_r;
b_state[1] = b;
r[mti] = (double)(b_r + b) * 2.328306436538696E-10;
}
} else {
if (!state_not_empty) {
memset(&c_state[0], 0, 625U * sizeof(unsigned int));
b_r = 5489U;
c_state[0] = 5489U;
for (mti = 0; mti < 623; mti++) {
b_r = ((b_r ^ b_r >> 30U) * 1812433253U + mti) + 1U;
c_state[mti + 1] = b_r;
}

c_state[624] = 624U;
state_not_empty = true;
}

for (mti = 0; mti < 2000; mti++) {
r[mti] = eml_rand_mt19937ar(c_state);
}
}
}

/* End of code generation (rand.cpp) */

randi.cpp—生成随机数矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Include files */
#include <cmath>
#include "rt_nonfinite.h"
#include "solve.h"
#include "randi.h"
#include "rand.h"

/* Function Definitions */
void randi(double r[2000])
{
int k;
b_rand(r);
for (k = 0; k < 2000; k++) {
r[k] = -100000.0 + std::floor(r[k] * 200001.0);
}
}

fileManager.cpp—文件管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/* Include files */
#include "rt_nonfinite.h"
#include "solve.h"
#include "fileManager.h"
#include "fclose.h"
#include "solve_rtwutil.h"
#include "solve_data.h"

/* Function Declarations */
static signed char filedata();

/* Function Definitions */
static signed char filedata()
{
signed char f;
int k;
boolean_T exitg1;
f = 0;
k = 0;
exitg1 = false;
while ((!exitg1) && (k < 20)) {
if (eml_openfiles[k] == NULL) {
f = (signed char)(k + 1);
exitg1 = true;
} else {
k++;
}
}

return f;
}

signed char cfopen(const char * cfilename, const char * cpermission)
{
signed char fileid;
signed char j;
FILE * filestar;
int i0;
fileid = -1;
j = filedata();
if (j >= 1) {
filestar = fopen(cfilename, cpermission);
if (filestar != NULL) {
eml_openfiles[j - 1] = filestar;
eml_autoflush[j - 1] = true;
i0 = j + 2;
if (i0 > 127) {
i0 = 127;
}

fileid = (signed char)i0;
}
}

return fileid;
}

void fileManager(double varargin_1, FILE * *f, boolean_T *a)
{
signed char fileid;
fileid = (signed char)rt_roundd_snf(varargin_1);
if ((fileid > 22) || (fileid < 0) || (varargin_1 != fileid)) {
fileid = -1;
}

if (fileid >= 3) {
*f = eml_openfiles[fileid - 3];
*a = eml_autoflush[fileid - 3];
} else if (fileid == 0) {
*f = stdin;
*a = true;
} else if (fileid == 1) {
*f = stdout;
*a = true;
} else if (fileid == 2) {
*f = stderr;
*a = true;
} else {
*f = NULL;
*a = true;
}
}

void filedata_init()
{
FILE * a;
int i;
a = NULL;
for (i = 0; i < 20; i++) {
eml_autoflush[i] = false;
eml_openfiles[i] = a;
}
}

fclose.cpp—关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*--fclose.cpp--*/
#include "rt_nonfinite.h"
#include "solve.h"
#include "fclose.h"
#include "solve_rtwutil.h"
#include "solve_data.h"

void b_fclose(double fileID)
{
signed char fileid;
signed char b_fileid;
FILE * filestar;
int cst;
fileid = (signed char)rt_roundd_snf(fileID);
if ((fileid > 22) || (fileid < 0) || (fileID != fileid)) {
fileid = -1;
}

b_fileid = fileid;
if (fileid < 0) {
b_fileid = -1;
}

if (b_fileid >= 3) {
filestar = eml_openfiles[b_fileid - 3];
} else if (b_fileid == 0) {
filestar = stdin;
} else if (b_fileid == 1) {
filestar = stdout;
} else if (b_fileid == 2) {
filestar = stderr;
} else {
filestar = NULL;
}

if ((filestar != NULL) && (fileid >= 3)) {
cst = fclose(filestar);
if (cst == 0) {
eml_openfiles[fileid - 3] = NULL;
eml_autoflush[fileid - 3] = true;
}
}
}