使用 Matlab Coder 的 C++ 兼容函数将整数转换为字符串
Convert integer to string with C++ compatible function for Matlab Coder
我正在使用 Matlab Coder 将一些 Matlab 代码转换为 C++,但是我在将整数转换为字符串时遇到了问题。
int2str()
不支持代码生成,因此我必须找到其他方法将整数转换为字符串。我试过谷歌搜索,但没有成功。这可能吗?
这可以手动完成(虽然非常痛苦)
function s = thePainfulInt2Str( n )
s = '';
is_pos = n > 0; % //save sign
n = abs(n); %// work with positive
while n > 0
c = mod( n, 10 ); % get current character
s = [uint8(c+'0'),s]; %// add the character
n = ( n - c ) / 10; %// "chop" it off and continue
end
if ~is_pos
s = ['-',s]; %// add the sign
end
sprintf
是另一个非常基本的函数,因此它也可能适用于 C++:
x = int64(1948)
str = sprintf('%i',x)
也是int2str
使用的底层函数。
根据这个 comprehensive list of supported functions, as pointed out by Matt in the comments, sprintf
is not supported, which is surprising. However there is the undocumented helper function (therefore not in the list) sprintfc
这似乎有效并且可以等效地使用:
str = sprintfc('%i',x)
编辑: 从 MATLAB R2018a 开始,MATLAB Coder 支持 sprintf
代码生成。
R2018a 之前的答案
您还可以使用 coder.ceval
调用 C 运行时 sprintf
或 snprintf
。这也有利于使支持浮点输入变得容易。您还可以通过调整格式字符串来根据需要更改格式。
假设您的编译器提供 snprintf
可以使用:
function s = cint2str(x)
%#codegen
if coder.target('MATLAB')
s = int2str(x);
else
coder.cinclude('<stdio.h>');
assert(isfloat(x) || isinteger(x), 'x must be a float or an integer');
assert(x == floor(x) && isfinite(x), 'x must be a finite integer value');
if isinteger(x)
switch class(x)
% Set up for Win64, change to match your target
case {'int8','int16','int32'}
fmt = '%d';
case 'int64'
fmt = '%lld';
case {'uint8','uint16','uint32'}
fmt = '%u';
otherwise
fmt = '%llu';
end
else
fmt = '%.0f';
end
% NULL-terminate for C
cfmt = [fmt, 0];
% Set up external C types
nt = coder.opaque('int','0');
szt = coder.opaque('size_t','0');
NULL = coder.opaque('char*','NULL');
% Query length
nt = coder.ceval('snprintf',NULL,szt,coder.rref(cfmt),x);
n = cast(nt,'int32');
ns = n+1; % +1 for trailing null
% Allocate and format
s = coder.nullcopy(blanks(ns));
nt = coder.ceval('snprintf',coder.ref(s),cast(ns,'like',szt),coder.rref(cfmt),x);
assert(cast(nt,'int32') == n, 'Failed to format string');
end
请注意,您可能需要调整格式字符串以匹配您 运行 所在的硬件,因为这假定 long long
可用并将 64 位整数映射到它.
我使用以下解决方法来启用 sprintf
以便与 Matlab Coder 一起使用:
1) 创建以下名为 "sprintf.m" 的 m 文件,最好在不在您的 Matlab 路径上的文件夹中:
function s = sprintf(f, varargin)
if (coder.target('MATLAB'))
s = builtin('sprintf',f,varargin{:});
elseif (coder.target('MEX'))
s = builtin('sprintf',f,varargin{:});
else
coder.cinclude('stdio.h');
s = char(zeros(1024,1));
cf = [f,0]; % NULL-terminated string for use in C
coder.ceval('sprintf_s', coder.ref(s(1)), int32(1024), coder.rref(cf(1)), varargin{:});
end
2) 确保 sprintf
未通过 coder.extrinsic
指定为外部
3) 指定包含新创建的文件夹"sprintf.m"作为生成代码时的附加包含目录。如果您使用 codegen
函数,则可以通过 -I
开关来完成。如果您使用 Coder 应用程序,可以在 "Generate" 选项卡的 "More Settings -> Custom Code -> Additional include directories" 下完成。
4) int 转 string 如下:s=sprintf('%d',int32(n));
备注:
- 指定的 "sprintf.m" 隐藏内置
sprintf
函数,并在您每次从生成的代码调用 sprintf
时代替内置函数执行。通过将此文件放在不在 Matlab 路径上的文件夹中,您可以避免从 Matlab 中对 运行 所做的其他代码调用它。 coder.target
调用还有助于导航回内置函数,以防在普通 Matlab 会话或 MEX 文件中调用它。
- 上面的代码将结果限制为 1023 个字符(末尾需要一个终止零)。如果结果超过此值,对
sprintf_s
的调用指示 C++ 编译器抛出 运行time 异常。这可以防止内存损坏,内存损坏通常要晚得多才能被发现,并且更难追溯到有问题的调用。此限制可以根据您自己的要求进行修改。
- 数字类型在传递给
sprintf
之前必须转换为正确的 class,例如强制转换为 int32
以匹配格式字符串中的 %d
。将 fprintf
与 Matlab Coder 一起使用时,此要求相同。但是,在 fprintf
的情况下,Matlab Coder 会为您捕获类型错误。对于 sprintf
,C++ 编译器可能会失败或生成的字符串可能包含错误。
- 字符串参数必须手动以 NULL 结尾才能在 C 调用中使用,因为 Matlab Coder 不会自动执行此操作(感谢 Ryan Livingston 指出了这一点)。上面的代码确保格式字符串
f
以 NULL 结尾,但其他字符串参数的 NULL 结尾仍然由调用函数负责。
- 此代码已在 Windows 平台上使用 Visual Studio C++ 编译器和 Matlab R2016a(Matlab Coder 3.1)进行测试,但预计也可以在大多数其他环境中运行。
我正在使用 Matlab Coder 将一些 Matlab 代码转换为 C++,但是我在将整数转换为字符串时遇到了问题。
int2str()
不支持代码生成,因此我必须找到其他方法将整数转换为字符串。我试过谷歌搜索,但没有成功。这可能吗?
这可以手动完成(虽然非常痛苦)
function s = thePainfulInt2Str( n )
s = '';
is_pos = n > 0; % //save sign
n = abs(n); %// work with positive
while n > 0
c = mod( n, 10 ); % get current character
s = [uint8(c+'0'),s]; %// add the character
n = ( n - c ) / 10; %// "chop" it off and continue
end
if ~is_pos
s = ['-',s]; %// add the sign
end
sprintf
是另一个非常基本的函数,因此它也可能适用于 C++:
x = int64(1948)
str = sprintf('%i',x)
也是int2str
使用的底层函数。
根据这个 comprehensive list of supported functions, as pointed out by Matt in the comments, sprintf
is not supported, which is surprising. However there is the undocumented helper function (therefore not in the list) sprintfc
这似乎有效并且可以等效地使用:
str = sprintfc('%i',x)
编辑: 从 MATLAB R2018a 开始,MATLAB Coder 支持 sprintf
代码生成。
R2018a 之前的答案
您还可以使用 coder.ceval
调用 C 运行时 sprintf
或 snprintf
。这也有利于使支持浮点输入变得容易。您还可以通过调整格式字符串来根据需要更改格式。
假设您的编译器提供 snprintf
可以使用:
function s = cint2str(x)
%#codegen
if coder.target('MATLAB')
s = int2str(x);
else
coder.cinclude('<stdio.h>');
assert(isfloat(x) || isinteger(x), 'x must be a float or an integer');
assert(x == floor(x) && isfinite(x), 'x must be a finite integer value');
if isinteger(x)
switch class(x)
% Set up for Win64, change to match your target
case {'int8','int16','int32'}
fmt = '%d';
case 'int64'
fmt = '%lld';
case {'uint8','uint16','uint32'}
fmt = '%u';
otherwise
fmt = '%llu';
end
else
fmt = '%.0f';
end
% NULL-terminate for C
cfmt = [fmt, 0];
% Set up external C types
nt = coder.opaque('int','0');
szt = coder.opaque('size_t','0');
NULL = coder.opaque('char*','NULL');
% Query length
nt = coder.ceval('snprintf',NULL,szt,coder.rref(cfmt),x);
n = cast(nt,'int32');
ns = n+1; % +1 for trailing null
% Allocate and format
s = coder.nullcopy(blanks(ns));
nt = coder.ceval('snprintf',coder.ref(s),cast(ns,'like',szt),coder.rref(cfmt),x);
assert(cast(nt,'int32') == n, 'Failed to format string');
end
请注意,您可能需要调整格式字符串以匹配您 运行 所在的硬件,因为这假定 long long
可用并将 64 位整数映射到它.
我使用以下解决方法来启用 sprintf
以便与 Matlab Coder 一起使用:
1) 创建以下名为 "sprintf.m" 的 m 文件,最好在不在您的 Matlab 路径上的文件夹中:
function s = sprintf(f, varargin)
if (coder.target('MATLAB'))
s = builtin('sprintf',f,varargin{:});
elseif (coder.target('MEX'))
s = builtin('sprintf',f,varargin{:});
else
coder.cinclude('stdio.h');
s = char(zeros(1024,1));
cf = [f,0]; % NULL-terminated string for use in C
coder.ceval('sprintf_s', coder.ref(s(1)), int32(1024), coder.rref(cf(1)), varargin{:});
end
2) 确保 sprintf
未通过 coder.extrinsic
3) 指定包含新创建的文件夹"sprintf.m"作为生成代码时的附加包含目录。如果您使用 codegen
函数,则可以通过 -I
开关来完成。如果您使用 Coder 应用程序,可以在 "Generate" 选项卡的 "More Settings -> Custom Code -> Additional include directories" 下完成。
4) int 转 string 如下:s=sprintf('%d',int32(n));
备注:
- 指定的 "sprintf.m" 隐藏内置
sprintf
函数,并在您每次从生成的代码调用sprintf
时代替内置函数执行。通过将此文件放在不在 Matlab 路径上的文件夹中,您可以避免从 Matlab 中对 运行 所做的其他代码调用它。coder.target
调用还有助于导航回内置函数,以防在普通 Matlab 会话或 MEX 文件中调用它。 - 上面的代码将结果限制为 1023 个字符(末尾需要一个终止零)。如果结果超过此值,对
sprintf_s
的调用指示 C++ 编译器抛出 运行time 异常。这可以防止内存损坏,内存损坏通常要晚得多才能被发现,并且更难追溯到有问题的调用。此限制可以根据您自己的要求进行修改。 - 数字类型在传递给
sprintf
之前必须转换为正确的 class,例如强制转换为int32
以匹配格式字符串中的%d
。将fprintf
与 Matlab Coder 一起使用时,此要求相同。但是,在fprintf
的情况下,Matlab Coder 会为您捕获类型错误。对于sprintf
,C++ 编译器可能会失败或生成的字符串可能包含错误。 - 字符串参数必须手动以 NULL 结尾才能在 C 调用中使用,因为 Matlab Coder 不会自动执行此操作(感谢 Ryan Livingston 指出了这一点)。上面的代码确保格式字符串
f
以 NULL 结尾,但其他字符串参数的 NULL 结尾仍然由调用函数负责。 - 此代码已在 Windows 平台上使用 Visual Studio C++ 编译器和 Matlab R2016a(Matlab Coder 3.1)进行测试,但预计也可以在大多数其他环境中运行。