-
Notifications
You must be signed in to change notification settings - Fork 450
/
ccodefunctionstring.m
238 lines (209 loc) · 7.74 KB
/
ccodefunctionstring.m
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
%CCODEFUNCTIONSTRING Converts a symbolic expression into a C-code function
%
% [FUNSTR, HDRSTR] = ccodefunctionstring(SYMEXPR, ARGLIST) returns a string
% representing a C-code implementation of a symbolic expression SYMEXPR.
% The C-code implementation has a signature of the form:
%
% void funname(double[][n_o] out, const double in1,
% const double* in2, const double[][n_i] in3);
%
% depending on the number of inputs to the function as well as the
% dimensionality of the inputs (n_i) and the output (n_o).
% The whole C-code implementation is returned in FUNSTR, while HDRSTR
% contains just the signature ending with a semi-colon (for the use in
% header files).
%
% Options::
% 'funname',name Specify the name of the generated C-function. If
% this optional argument is omitted, the variable name
% of the first input argument is used, if possible.
% 'output',outVar Defines the identifier of the output variable in the C-function.
% 'vars',varCells The inputs to the C-code function must be defined as a cell array. The
% elements of this cell array contain the symbolic variables required to
% compute the output. The elements may be scalars, vectors or matrices
% symbolic variables. The C-function prototype will be composed accoringly
% as exemplified above.
% 'flag',sig Specifies if function signature only is generated, default (false).
%
% Example::
% % Create symbolic variables
% syms q1 q2 q3
%
% Q = [q1 q2 q3];
% % Create symbolic expression
% myrot = rotz(q3)*roty(q2)*rotx(q1)
%
% % Generate C-function string
% [funstr, hdrstr] = ccodefunctionstring(myrot,'output','foo', ...
% 'vars',{Q},'funname','rotate_xyz')
%
% Notes::
% - The function wraps around the built-in Matlab function 'ccode'. It does
% not check for proper C syntax. You must take care of proper
% dimensionality of inputs and outputs with respect to your symbolic
% expression on your own. Otherwise the generated C-function may not
% compile as desired.
%
% Author::
% Joern Malzahn, (joern.malzahn@tu-dortmund.de)
%
% See also ccode, matlabFunction.
% Copyright (C) 2012-2018, by Joern Malzahn
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB. If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com
function [funstr hdrstr] = ccodefunctionstring(f,varargin)
% option defaults
opt.funname = inputname(1);
opt.output{1} = zeros(size(f));
opt.outputName{1} = inputname(1);
opt.flag = 0;
if isempty(opt.outputName{1})
opt.outputName{1} = 'myout';
end
opt.vars = {};
% tb_optparse is not applicable here,
% since handling cell inputs and extracting input variable names is
% required.
% Thus, scan varargin manually:
if mod(nargin,2)==0
error('CodeGenerator:codefunctionstring:wrongArgumentList',...
'Wrong number of elements in the argument list.');
end
for iArg = 1:2:nargin-1
switch lower(varargin{iArg})
case 'funname'
opt.funname = varargin{iArg+1};
case 'output'
if ~isempty(varargin{iArg+1})
opt.outputName{1} = varargin{iArg+1};
end
case 'vars'
opt.vars = varargin{iArg+1};
case 'flag'
opt.flag = varargin{iArg+1};
otherwise
error('ccodefunctionstring:unknownArgument',...
['Argument ',inputname(iArg),' unknown.']);
end
end
nOut = numel(opt.output);
nIn = numel(opt.vars);
%% Function signature
funstr = sprintf('void %s(', opt.funname);
initstr = '';
% outputs
for iOut = 1:nOut
tmpOutName = opt.outputName{iOut};
tmpOut = opt.output{iOut};
if ~isscalar(tmpOut);
funstr = [funstr, sprintf('double %s[][%u]', tmpOutName, size(tmpOut,1) ) ];
for iRow = 1:size(tmpOut,1)
for iCol = 1:size(tmpOut,2)
initstr = sprintf(' %s %s[%u][%u]=0;\n',initstr,tmpOutName,iCol-1,iRow-1);
end
end
else
funstr = [funstr, sprintf('double %s', tmpOutName ) ];
end
% separate argument list by commas
if ( iOut ~= nOut ) || ( nIn > 0 )
funstr = [funstr,', '];
end
end
% inputs
for iIn = 1:nIn
tmpInName = ['input',num2str(iIn)];%opt.varsName{iIn};
tmpIn = opt.vars{iIn};
% treat different dimensionality of input variables
if isscalar(tmpIn)
funstr = [funstr, sprintf('const double %s', tmpInName ) ];
elseif isvector(tmpIn)
funstr = [funstr, sprintf('const double* %s', tmpInName ) ];
elseif ismatrix(tmpIn)
funstr = [funstr, sprintf('const double %s[][%u]', tmpInName, size(tmpIn,2) ) ];
else
error('ccodefunctionstring:UnsupportedOutputType', 'Unsupported datatype for %s', tmpOutName)
end
% separate argument list by commas
if ( iIn ~= nIn )
funstr = [funstr,', '];
end
end
funstr = [funstr,sprintf('%s', ')')];
% finalize signature for the use in header files
if nargout > 1
hdrstr = [funstr,sprintf('%s', ';')];
end
if opt.flag
return; %% STOP IF FLAG == TRUE
end
% finalize signature for use in function definition
funstr = [funstr,sprintf('%s', '{')];
funstr = sprintf('%s\n%s',funstr,sprintf('%s', ' ') ); % empty line
%% Function body
% input paramter expansion
for iIn = 1:nIn
tmpInName = ['input',num2str(iIn)];%opt.varsName{iIn};
tmpIn = opt.vars{iIn};
% for scalars
% -> do nothing
% for vectors
if ~isscalar(tmpIn) && isvector(tmpIn)
nEl = numel(tmpIn);
for iEl = 1:nEl
funstr = sprintf('%s\n%s',...
funstr,...
sprintf(' double %s = %s[%u];', char(tmpIn(iEl)), tmpInName,iEl-1 ));
end
% for matrices
elseif ~isscalar(tmpIn) && ~isvector(tmpIn) && ismatrix(tmpIn)
nRow = size(tmpIn,1);
nCol = size(tmpIn,2);
for iRow = 1:nRow
for iCol = 1:nCol
funstr = sprintf('%s\n%s',...
funstr,...
sprintf(' double %s%u%u = %s[%u][%u];', char(tmpIn(iRow,iCol)), iRow, iCol, tmpInName{iIn},iRow-1,iCol-1 ));
end
end
end
end
funstr = sprintf('%s\n%s',...
funstr,...
sprintf('%s', ' ') );
funstr = sprintf('%s\n%s',...
funstr,...
sprintf('%s\n\n', initstr) );
% Actual code
% use f.' here, because of column/row indexing in C
codestr = '';
if ~isequal(f, sym(zeros(size(f))))
eval([opt.outputName{1}, ' = f.''; codestr = ccode(',opt.outputName{1},');'])
end
if isscalar(f)
% in the case of scalar expressions the resulting ccode always
% begins with ' t0'. Replace that with the desired name.
codestr = strrep(codestr,'t0',opt.outputName{1});
end
funstr = sprintf('%s\n%s',...
funstr,...
codestr );
funstr = sprintf('%s\n%s',...
funstr,sprintf('%s', '}') );
funstr = sprintf('%s\n%s',...
funstr,sprintf('%s', ' ') ); % empty line