Convert integer to string with C++ compatible function for Matlab Coder
Asked Answered
P

4

6

I'm using Matlab Coder to convert some Matlab code to C++, however I'm having trouble converting intergers to strings.

int2str() is not supported for code generation, so I must find some other way to convert ints to strings. I've tried googling it, without success. Is this even possible?

Penult answered 23/7, 2015 at 7:52 Comment(2)
Is num2str supported? It's the most general function of this type.Judicator
num2str is unfortunately not supported either.Penult
E
8

This can be done manually (very painful, though)

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
Equilateral answered 23/7, 2015 at 8:17 Comment(2)
I think you need to change the while condition to n>0Egeria
Minor nit, this prints an incorrect string for intmin of the signed integer types since abs(n) will saturate. For example abs(intmin('int32')) will saturate so that -intmax('int32') is printed. Could add a check for intmin and then add one to s(end).Involve
R
7

sprintf is another very basic function, so it possibly works in C++ as well:

x = int64(1948)
str = sprintf('%i',x)

It is also the underlying function used by int2str.


According to this 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 which seems to work and can be used equivalently:

str = sprintfc('%i',x)
Rallentando answered 23/7, 2015 at 8:28 Comment(6)
That's what I thought as well. Before writing an answer, I checked the list here and sprintf does not seem to be supported.Fredkin
sprintf is not supported either.Penult
@Penult how about the undocumented sprintfc - can you check?Rallentando
sprintfc actually seems to work. I guess that is the smoothest solution then. Thanks! @thewaywewalkPenult
Actually I found out now that sprintfc does not work. It didn't warn at first, for some reason, but now it does. So I suppose you have to do this manually. @thewaywewalkPenult
@Penult good to know! You should consider accepting Shai's Answer than, as that one helped you.Rallentando
I
2

Edit: As of MATLAB R2018a, sprintf is supported for code generation by MATLAB Coder.

Pre R2018a Answer

You could also call the C runtime sprintf or snprintf using coder.ceval. This has the benefit of making supporting floating point inputs easy as well. You can also change the formatting as desired by tweaking the format string.

Supposing that your compiler provides snprintf one could use:

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

Note that you'll possibly need to tweak the format string to match the hardware on which you're running since this assumes that long long is available and maps 64-bit integers to it.

Involve answered 24/7, 2015 at 12:48 Comment(0)
S
2

I use the following workaround to enable sprintf for general use with Matlab Coder:

1) Create the following m-file named "sprintf.m", preferably in a folder NOT on your Matlab path:

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) Ensure that sprintf is not specified as extrinsic via coder.extrinsic

3) Specify the folder containing the newly created "sprintf.m" as additional include directory when generating code. If you use the codegen function, this can be done via the -I switch. If you use the Coder App, this can be done under "More Settings -> Custom Code -> Additional include directories" from the "Generate" tab.

4) Convert from int to string as follows: s=sprintf('%d',int32(n));

Notes:

  • The specified "sprintf.m" shadows the built-in sprintf function and executes instead of the built-in function every time you call sprintf from generated code. By placing this file in a folder that is not on the Matlab path, you avoid calling it from other code made to run in Matlab. The coder.target call also helps to navigate back to the built-in function in case this gets called in a normal Matlab session or from a MEX file.
  • The code above limits the result to 1023 characters (a terminating zero is required at the end). The call to sprintf_s instructs the C++ compiler to throw a runtime exception if the result exceeds this value. This prevents memory corruption that is often only caught much later and is harder to trace back to the offending call. This limit can be modified to your own requirements.
  • Numeric types must be cast to the correct class before passing them to sprintf, e.g. cast to int32 to match a %d in the format string. This requirement is the same when using fprintf with Matlab Coder. However, in the fprintf case, Matlab Coder catches type errors for you. For sprintf, the C++ compiler may fail or the resulting string may contain errors.
  • String arguments must be NULL-terminated manually to be used in C calls, as Matlab Coder does not do this automatically (credit to Ryan Livingston for pointing this out). The code above ensures that the format string f is NULL-terminated, but NULL-termination of other string arguments remains the responsibility of the calling function.
  • This code was tested on a Windows platform with a Visual Studio C++ compiler and Matlab R2016a (Matlab Coder 3.1), but is expected to work in most other environments as well.
Sump answered 20/10, 2016 at 13:1 Comment(1)
Don't forget to null-terminate any arguments which will be C strings: cf = [f, 0]; coder.ceval(...,coder.rref(cf(1)));. MATLAB Coder produced character arrays are not null-terminated. Failing to do so will result in undefined behavior in the generated code.Involve

© 2022 - 2024 — McMap. All rights reserved.