Procedure Put from numeric type to String, but not a function?

I found this subprogram in all numeric IO package:

procedure Put(To   : out String;
                    Item : in Num;
                    Base : in Number_Base := Default_Base)

I wanted to ask… am I missing something here ? An unconstrained array (String), as an out parameter, when they should have made it a function ? What went through the designers’ head ?
This procedure is borderline unusable, since we can’t predict the size the String should have.
It is a shame as I needed a convenient way to convert between bases.

Integer_IO is focused more on sending the value somewhere (to a file, stream, string, etc) than converting it. There’s ways to utilize Put to store it and return it in a function, but it’s not very efficient.

My solution was to use Simple Component’s Image:

function Image
         (  Value   : Number'Base;
            Base    : NumberBase := 10;
            PutPlus : Boolean    := False
         )  return String;
2 Likes

Did you mean this version:

      procedure Put(To   : out String;
                    Item : in Num;
                    Base : in Number_Base := Default_Base)

If so, the reason for that is so you can do formatted number text. Say you are printing out register values to see what is going on in your processor. You want to ensure you line them all up for display. You know you are working with 32bit numbers and want to display in hex, so you pick a string that can show: “16#XXXX#” or an 8 character string. If a number is too small it will pad it for you, lining every thing up:

16#0000#
16#0001#
-- etc
1 Like

Did you copy the wrong signature? I don’t see a String there:

I think I know what subprogram you mean, though, as I recently used it. In my case, I overestimated the size and, after the Put, used Fixed_Strings.Trim on the result.

On other occasions I’ve used 'Image and then trimmed it. If you don’t want to use @AJ-Ianozi’s suggestion, try that.

Since I’m not among the designers I can only speculate, but:

  • the Put subprograms in the various IO packages are all procedures, and consistency is generally helpful; and
  • there’s a use case where you may want to Put multiple numbers into a long String.

Ada didn’t get functions with out/in out parameters until Ada2012.
That method is essentially for “filling in” a fixed-length string with the integer-value.

Probably things closer to COBOL than you realize.
Tabular data and all.

Sure you can, it just involves understanding your value-ranges and maximal digit-length, which means using some logarithms.

As a naive approach (not optimized) I’ve generally set my string length equal to the bit size of the number type I am printing. I don’t need that much, but I know it will always be enough and it isn’t a super large chunk to allocate in most applicaitons. If I am printing a based literal, I add 4 to that for the 16# and `#1 that is also printed.

Sorry it was late and I took the wrong declaration.
Ah, cobolt… well that’s unfortunate. Yes it does padding, but even that should be parameterizable. There are plenty of IO functions with a Padding and Justify parameters, why not there ?

To fill a string with substring of unknowable size, I would probably use recursion. But there is use for both a function and a procedure.
The problem is more when the string is too small. Put does the conversion, so it has the algorithm already. We should have

function Convert_Base (Item: Num;
         Base: Number_Base := Default_Base) return String;

I’ll ask the ARG group, who knows…

You may consider:

   generic
      type T is range <>;
   function Integer_Image (Item : T; Base : in Number_Base := 10; Prefix : Number_Prefix := None) return UXString;
   -- Return the conversion of the integer Item into string with respect of specified Base and Prefix

From UXStrings.Conversions.

1 Like

I don’t disagree with the preference on API. I too would prefer some function centric API, even if it was additional. I was more addressing your comments on it being unusable, so I was trying to explain the use. There are many situations where you can predict the size of the string and also you can always predict a max size for any integer type. But whether you want a separate parameter to specify width or just have the user specify the width via the actual size of the string, both do the same thing, it’s more a matter of preference.

if you want a function version to do it that you can use in your own libraries, you can do something like:

function Put(Width : Positive; Item : Num; Base : Number_Base) return String
is begin
   return Result : String(1..Width) do
      Put(To => Result, Item => Num, Base => Base);
   end return;
end Put;

if you want one that truncates instead of tosses an exception:

function Put(Width : Positive; Item : Num; Base : Number_Base) return String
is 
   Result : String(1..Num'Size + 4);
   First : constant Positive := Result'Last - (Natural(Width)-1);
begin
   Put(To => Result, Item => Num, Base => Base);
   return Result(First .. Result'Last);
end Put;

*** Untested, just typed out

I think you can do without predicting the size nor fixing a max size, by using recursion. I need to work on it.