Lazarus

Free Pascal => General => Topic started by: lagprogramming on January 17, 2022, 05:31:43 pm

Title: TryStrToInt declaration question
Post by: lagprogramming on January 17, 2022, 05:31:43 pm
Hi!
Regarding the "TryStrTo..." functions, we have a single function name to convert strings to single, double or extended, it's TryStrToFloat. Not all CPUs support extended, double, some not even single.
Why do we have TryStrToInt for 32bit variables, TryStrToInt64 for 64bit signed variables and TryStrToQWord for 64bit unsigned variables?
I found this situation when I realized that calling TryStrToInt with a sizeuint parameter may lead to errors when I target x86_64.
I've looked at rtl documentation and I've seen:
Declaration: function TryStrToInt(const s: string; out i: LongInt) : Boolean
Declaration: function TryStrToInt64(const s: string; out i: Int64) : Boolean
Declaration: function TryStrToQWord(const s: string; out Q: QWord) : Boolean
How comes that for 32bit we have a single function(TryStrToInt) with a longint parameter, but for 64bit we have a function for signed integers(TryStrToInt64) and a different function for unsigned integers(TryStrToQWord)?
Thank you!
Title: Re: TryStrToInt declaration question
Post by: ASerge on January 17, 2022, 08:02:04 pm
For historical reasons.
The Int64 type was first introduced in Delphi4. And at the same time, the StrToInt64 function was added in addition to the existing StrToInt. Since overlapping functions (introduce in Delphi4 also) do not differ in the type of results, the names were chosen of course different. It is also worth noting that for 32-bit Intel processors, 32-bit integers and 10-bit floating-point numbers (extended) were native. Therefore, the functions StrToInt: Integer and StrToFloat:Extended were native, but StrToInt64: Int64 is optional. In later versions Delphi, TryStrTo... functions were added. With the preservation of names, of course.
The StrToQWord function was added in FPC because the QWord type did not fit into Int64. A similar StrToUInt64 function was also added, but later, in Delphi.
Title: Re: TryStrToInt declaration question
Post by: ASerge on January 17, 2022, 08:12:18 pm
Of course, if you don't like a lot of names, you can overload the functions:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$LONGSTRINGS ON}
  3.  
  4. uses SysUtils;
  5.  
  6. function TryStrToInt(const S: string; out Value: Int64): Boolean; inline; overload;
  7. begin
  8.   Result := SysUtils.TryStrToInt64(S, Value);
  9. end;
  10.  
  11. function TryStrToInt(const S: string; out Value: QWord): Boolean; inline; overload;
  12. begin
  13.   Result := SysUtils.TryStrToQWord(S, Value);
  14. end;
  15.  
  16. var
  17.   I32: Int32;
  18.   I64: Int64;
  19.   Q: QWord;
  20. begin
  21.   TryStrToInt('1', I32); // Call SysUtils.TryStrToInt
  22.   TryStrToInt('1', I64); // Call SysUtils.TryStrToInt64
  23.   TryStrToInt('1', Q); // Call SysUtils.TryStrToQWord
  24. end.
Title: Re: TryStrToInt declaration question
Post by: lagprogramming on January 18, 2022, 01:30:52 pm
   Let's assume that a developer has a code like this:
Code: Pascal  [Select][+][-]
  1. function foo(StringVariable:string; var Number:integer):boolean;
  2. var
  3.   IntegerVariable: integer;
  4. begin
  5.   {Pascal code}
  6.   if TryStrToInt(StringVariable,IntegerVariable) then...
  7.   {Additional pascal code}
  8.   Number:=IntegerVariable;
  9. end;

   In order to make the above code work nicely and native with 64bit CPUs it's not enough to change the integer types to sizeint.
   According to the slogan "Write once, compile anywhere", if the code in written in a 32bit environment(which means sizeint would have the same size as integer) each and every developer has to realize that TryStrToInt won't work as they expect when the code is compiled in a 64bit environment. Those developers needs some luck to realize that. If the developers realize that their code has a problem in using this function, they must come with a workaround. One workaround is the one presented by ASerge, other approaches might be using conditional compilation syntax like {$IF....} or using the Val routine directly. The approach involving workarounds is far from being crosscompile friendly.

   Wouldn't be better to avoid pushing each and every developer to write workarounds in their code by making TryStrToInt also accept 64bit integers and then modify existing code in TryStrToInt64 and TryStrToQWord to make it call TryStrToInt? With this approach we would have a clean way of using TryStrToInt routine similar to TryStrToFloat while keeping existing written pascal code compatible, for historical reasons.
Title: Re: TryStrToInt declaration question
Post by: Seenkao on January 18, 2022, 01:49:44 pm
разработчикам в любом случае надо будет приводить тип данных к компилируемой архитектуре. Понятно дело, это не надо будет делать пользователю. Но вот подгонка типов данных под компилируемую архитектуру... этот не очень благодарное дело.

google translate:
developers in any case will need to cast the data type to the compiled architecture. Clearly, the user does not need to do this. But here's the adjustment of data types to the compiled architecture ... this is not a very rewarding job.
Title: Re: TryStrToInt declaration question
Post by: Zoran on January 18, 2022, 02:50:49 pm
I agree that you have a point. Only I think that, instead of unifying all to TryStrToInt overloads (could it create some (backwards-/Delphi-) compatibility problems?), perhaps the cleaner solution would be adding a new function TryStrToPtrInt to Sysutils.

Anyway, it's worth making a feature request in bug tracker; do it.
Then further discussion should go there. FPC developers will then decide which approach to take, if any.

And using val directly is not really a big problem.
Title: Re: TryStrToInt declaration question
Post by: Thaddy on January 18, 2022, 03:06:40 pm
Note that AFAIK the tryXXX family in trunk has been recently mutilated by the use of exceptions in its implementations. Just counter-intuitive to why those were designed and not Delphi compatible. This is currently only an issue for trunk. That is not "implementation detail", it misses the point of tryxxx.  >:D : Don't rely on exceptions when not needed.
As far as I can see, that is an introduction of dirty code in the RTL.

I hope one see's its ways....
Title: Re: TryStrToInt declaration question
Post by: Zoran on January 18, 2022, 03:25:30 pm
it misses the point of tryxxx.  >:D : Don't rely on exceptions when not needed.

Of course. StrToInt should call TryStrToInt and only then raise if needed. Not the other way around, first unnecessary raise then catch and discard it.
It's ridiculous.
Title: Re: TryStrToInt declaration question
Post by: Thaddy on January 18, 2022, 03:55:40 pm
I saw the Delphi Implementation, so can not provide an uninformed  - un-biased - patch.
I hope it has not made it into a release version.
But it defeats the point.
(btw: original Implementation was correct.)
Title: Re: TryStrToInt declaration question
Post by: Bart on January 18, 2022, 06:23:47 pm
it misses the point of tryxxx.  >:D : Don't rely on exceptions when not needed.

Of course. StrToInt should call TryStrToInt and only then raise if needed. Not the other way around, first unnecessary raise then catch and discard it.
It's ridiculous.

Both in 3.2.2 and in trunk the TryStrToXXX versions just call Val() and in trunk StrToxxx calls TryStrToXXX,whereas in 3.2.2 they call Val().
So, the TryStrToXXX functions do not call StrToXXX and then catch the exception.

Bart
Title: Re: TryStrToInt declaration question
Post by: MarkMLl on January 18, 2022, 10:48:00 pm
That is not "implementation detail", it misses the point of tryxxx.  >:D : Don't rely on exceptions when not needed.
As far as I can see, that is an introduction of dirty code in the RTL.

That's particularly unfortunate, since a few weeks ago somebody- neither a newcomer to the forum nor to the language- was criticising the entire library stack due to over-use of exceptions.

How on earth did it get past review? The World wonders...

MarkMLl
Title: Re: TryStrToInt declaration question
Post by: Bart on January 18, 2022, 10:59:03 pm
OK, so which TryStrToXXX functions in trunk have been "mutilated" to use exceptions?
AFAICS none of the TryStrToInt variants.

Bart
Title: Re: TryStrToInt declaration question
Post by: PascalDragon on January 19, 2022, 10:31:39 am
Note that AFAIK the tryXXX family in trunk has been recently mutilated by the use of exceptions in its implementations. Just counter-intuitive to why those were designed and not Delphi compatible. This is currently only an issue for trunk.

There is no issue, cause the Try… functions don't use exceptions.

That is not "implementation detail", it misses the point of tryxxx.  >:D : Don't rely on exceptions when not needed.
As far as I can see, that is an introduction of dirty code in the RTL.

That's particularly unfortunate, since a few weeks ago somebody- neither a newcomer to the forum nor to the language- was criticising the entire library stack due to over-use of exceptions.

How on earth did it get past review? The World wonders...

Nothing did get past review here, cause nothing like Thaddy mentioned happened.
Title: Re: TryStrToInt declaration question
Post by: MarkMLl on January 19, 2022, 11:00:26 am
Nothing did get past review here, cause nothing like Thaddy mentioned happened.

@Thaddy, would you like to explain what you think you've seen?

MarkMLl
Title: Re: TryStrToInt declaration question
Post by: lagprogramming on January 19, 2022, 11:26:25 am
   I've looked in the rtl documentation I have and I've seen the following declarations:
Code: Pascal  [Select][+][-]
  1. function IntToStr(Value: LongInt) : string
  2. function IntToStr(Value: Int64) : string
  3. function IntToStr(Value: QWord) : string
  4. function UIntToStr(Value: QWord) : string
  5. function UIntToStr(Value: Cardinal) : string
   Maybe function IntToStr(Value: QWord) : string does the same thing as function UIntToStr(Value: QWord) : string, I don't know.
   In addition to the fact that we have TryStrToFloat working with single, double and extended, the point presenting the above extras is that we have IntToStr functions working with 64bit integers, but we don't have TryStrToInt working with 64bit integers. This can't be good. There is no reason or hint to realize that contrary to the use of TryStrToFloat and IntToStr, a developer must use different function names when he wants to do what he expects from a TryStrToInt function.
Title: Re: TryStrToInt declaration question
Post by: PascalDragon on January 19, 2022, 02:01:26 pm
   I've looked in the rtl documentation I have and I've seen the following declarations:
Code: Pascal  [Select][+][-]
  1. function IntToStr(Value: LongInt) : string
  2. function IntToStr(Value: Int64) : string
  3. function IntToStr(Value: QWord) : string
  4. function UIntToStr(Value: QWord) : string
  5. function UIntToStr(Value: Cardinal) : string
   Maybe function IntToStr(Value: QWord) : string does the same thing as function UIntToStr(Value: QWord) : string, I don't know.

The UIntToStr overloads were added for Delphi compatibility.

   In addition to the fact that we have TryStrToFloat working with single, double and extended, the point presenting the above extras is that we have IntToStr functions working with 64bit integers, but we don't have TryStrToInt working with 64bit integers. This can't be good. There is no reason or hint to realize that contrary to the use of TryStrToFloat and IntToStr, a developer must use different function names when he wants to do what he expects from a TryStrToInt function.

You can just as well use the following, then you don't need to deal with the differences between TryStrToInt and TryStrToInt64:

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses
  4.   SysUtils;
  5.  
  6. var
  7.   s: SizeInt;
  8. begin
  9.   if SizeInt.TryParse('8481849', s) then
  10.     Writeln(s)
  11.   else
  12.     Writeln('Invalid number');
  13. end.
Title: Re: TryStrToInt declaration question
Post by: lagprogramming on January 19, 2022, 03:22:05 pm
Funny stuff. I've decided to write a new TryToWhateverInteger function and to my surprise I've found that almost 10 years ago I've written a TryToLongword function. This means that almost a decade a ago I've hit the same problem the only difference being that at that time the problem I've had was with 32bit unsigned integers and now with 64bit integers.  :-[ History repeating.
Title: Re: TryStrToInt declaration question
Post by: Bart on January 19, 2022, 03:51:55 pm
The problem being?

Bart
Title: Re: TryStrToInt declaration question
Post by: lagprogramming on January 19, 2022, 05:15:52 pm
Code: Pascal  [Select][+][-]
  1. function ErrorCompilingThis(const StringParam:string; out LongwordParam:longword):boolean;
  2. begin
  3. //Error at the following line: Call by var for arg no. 2 has to match exactly: Got "LongWord" expected "LongInt"
  4. result:=TryStrToInt(StringParam,LongwordParam);
  5. end;
  6.  
  7. procedure TForm1.Button1Click(Sender: TObject);
  8. var
  9.   LW1,LW2:LongWord;
  10. begin
  11. LW1:=LongWord(not(0));LW2:=0;
  12. //Obviously, same error in the following line
  13. if TryStrToInt(IntToStr(LW1),LW2) THEN
  14.    if LW1=LW2 then Form1.Caption:='OK';
  15. end;

   Now I see there is a TryStrToDWord function, probably it existed even at the time I've written my own TryStrToLongword. Totally counter-intuitive names in the rtl, really.

   Nowadays we have a generic function name TryStrToInt that supports multiple types of strings but when it comes to the integer parameter we're stuck with the longint one. In addition to this function name we have a dozen other functions but with precise names that do the same kind of thing as TryStrToInt and that support the same multiple types of strings but only one type of integer: TryStrToInt64, TryStrToQWord, TryStrToUInt64, TryStrToDWord and who knows how many others. If the function name has to identify the second parameter then TryStrToInt doesn't identify the LongInt parameter, it confuzes people into thinking that it's a function that applies to all sorts of integers like it applies to all sorts of strings.

   I've given the TryStrToFloat example, here is another one:
   TryStrToBool(const S: string; out Value: Boolean) : Boolean;
   TryStrToBool(const S: string; out Value: Boolean; const FormatSettings: TFormatSettings) : Boolean;
   The rtl doesn't have a TryStrToBool(const S: string; out Value: BYTEBOOL) : Boolean; declaration, neither a TryStrToLONGBOOL(const S: string; out Value: LONGBOOL) : Boolean declaration.

   Probably due to historical reasons we have a function with a generic name that should cover many types of strings and integers, but it doesn't. At least I've been mislead by the name.
Title: Re: TryStrToInt declaration question
Post by: ASBzone on February 08, 2022, 11:05:37 pm
Regarding the "TryStrTo..." functions, we have a single function name to convert strings to single, double or extended, it's TryStrToFloat.
Thank you!

I had an application that needed several numeric conversions, and at first I just used TryStrToFloat, and typecast to the appropriate integer type when necessary.

Yeah...  That was noticeably slower than staying within the fun realm of integers for integer results.
Title: Re: TryStrToInt declaration question
Post by: ASBzone on February 08, 2022, 11:19:27 pm
In addition to this function name we have a dozen other functions but with precise names that do the same kind of thing as TryStrToInt and that support the same multiple types of strings but only one type of integer: TryStrToInt64, TryStrToQWord, TryStrToUInt64, TryStrToDWord and who knows how many others.

It seems straightforward to me why you can have a single name for a myriad of functions when converting from IntegerX to String.  You're only looking for a single output type, and the parameter is going to be matched within the overloaded functions, so only one name is needed.

However, if you are going to turn a single string into *some* type of Integer, how will the computer know what kind of Integer you wanted to end up with?  You might have specific reasons for asking for Int32 even if using a QWord variable.   How could this ever be easily communicated without a specific function name?   
TinyPortal © 2005-2018