Jump to content
Sign in to follow this  
Rollo62

TStringHelper.IndexOfAny( array of string, ...): Why is this private ?

Recommended Posts

Posted (edited)

Hi there,

 

I'm looking for an IndexOfAny( array or string, ... ) function, and find that there is already one in the TStringHelper.

Unfortunately this is declared as a private function.

 

  TStringHelper = record helper for string
    ...
  private
    ...
    //<== NICE TO HAVE
    function IndexOfAny(const Values: array of string; var Index: Integer; StartIndex: Integer): Integer; overload;
    ..
  public
    ...
    // Maybe these bites with above declaration, as Char may interpreted as string too ?
    function IndexOfAny(const AnyOf: array of Char): Integer; overload;
    function IndexOfAny(const AnyOf: array of Char; StartIndex: Integer): Integer; overload;
    function IndexOfAny(const AnyOf: array of Char; StartIndex: Integer; Count: Integer): Integer; overload;
    ...
end;

I'm not realy sure why this function is hidden.

Maybe because Char and String might cause issues when mixing in a overload, I haven't checked what would happen when making this public too ?

 

 

 

Edited by Rollo62

Share this post


Link to post
4 hours ago, Rollo62 said:

Hi there,

 

I'm looking for an IndexOfAny( array or string, ... ) function, and find that there is already one in the TStringHelper.

Unfortunately this is declared as a private function.

 


  TStringHelper = record helper for string
    ...
  private
    ...
    //<== NICE TO HAVE
    function IndexOfAny(const Values: array of string; var Index: Integer; StartIndex: Integer): Integer; overload;
    ..
  public
    ...
    // Maybe these bites with above declaration, as Char may interpreted as string too ?
    function IndexOfAny(const AnyOf: array of Char): Integer; overload;
    function IndexOfAny(const AnyOf: array of Char; StartIndex: Integer): Integer; overload;
    function IndexOfAny(const AnyOf: array of Char; StartIndex: Integer; Count: Integer): Integer; overload;
    ...
end;

I'm not realy sure why this function is hidden.

Maybe because Char and String might cause issues when mixing in a overload, I haven't checked what would happen when making this public too ?

 

 

 

There would probably be a conflict in overload resolution.

 

Anyway, System.StrUtils contains IndexStr and IndexText functions that would serve your need here.

Share this post


Link to post

Thanks, Peter.

Thats what I assumed too, but is there any deeper explanation how overload resolution of Char and String really works, and how this could be solved ?

Maybe even the general resolution between Char and String.

 

I understand that a String can be made of one Char 'a', which may be resolved as Char or String, which leads to ambiguities.

Is there any clever trick to make this type references clear, and maybe even workable in overloads ?

- I can use casts ( but that won't work for overlaod either )

- I could define and cast to my own types, like MyChar and MyString ( which is also not very elegant )

- Maybe constants like #13, #10, #0 have more "char-type-ness" than their string '' conterparts, but in practice that doesn't help much.

 

Pascal has had the reputation of being the most type-safe language for quite some time, but just that char/string ambiguity has always left a bad taste, at least for me.

I always wondered if these types cannot be made more "type-safe" nicely somehow.

 

 

 

Share this post


Link to post
3 hours ago, Rollo62 said:

Thanks, Peter.

Thats what I assumed too, but is there any deeper explanation how overload resolution of Char and String really works, and how this could be solved ?

Maybe even the general resolution between Char and String.

 

I understand that a String can be made of one Char 'a', which may be resolved as Char or String, which leads to ambiguities.

Is there any clever trick to make this type references clear, and maybe even workable in overloads ?

- I can use casts ( but that won't work for overlaod either )

- I could define and cast to my own types, like MyChar and MyString ( which is also not very elegant )

- Maybe constants like #13, #10, #0 have more "char-type-ness" than their string '' conterparts, but in practice that doesn't help much.

 

Pascal has had the reputation of being the most type-safe language for quite some time, but just that char/string ambiguity has always left a bad taste, at least for me.

I always wondered if these types cannot be made more "type-safe" nicely somehow.

 

 

 

I think the core of the problem is an ambiguity inherent in the original language syntax inherited from the days of Turbo Pascal and earlier. 'x' can be interpreted either as a character constant or a string constant, and the compiler decides which it should be dependent on the context. A char is also assignment compatible with a variable or parameter of type string (but not vice versa).

I see no way to get around this, and changing these rules will never happen since they make sense and changing them would break heaps of older code.

 

Of course the compiler could use more stringent rules for overload resolution, e.g. require type identity and not only assignment compatibility. That would solve the conflict in the case you noticed but would be quite bothersome in other situations, requiring more overloaded versions of a function to be implemented. Think about what a type identity requirement would mean for overloaded functions taking numeric types as parameter. Do you really want to declare versions for byte, smallint, integer,  word, cardinal etc. ?

  • Like 2

Share this post


Link to post
2 hours ago, PeterBelow said:

Do you really want to declare versions for byte, smallint, integer,  word, cardinal etc.

There are times when this is desirable. 

 

It's a big weakness in my view that the type of a literal can be ambiguous. 

  • Like 3

Share this post


Link to post
5 hours ago, PeterBelow said:

Think about what a type identity requirement would mean for overloaded functions taking numeric types as parameter. Do you really want to declare versions for byte, smallint, integer,  word, cardinal etc. ?

Resolve to the overload that best match the parameter?

function Bar(Foo: byte): byte; overload;
function Bar(Foo: integer): integer; overload;
...
begin
  var b: byte := 1;
  var i: integer := 2;

  Bar(b); // Use Bar(byte)
  Bar(i); // Use Bar(integer)

  Bar(byte(3)); // Use Bar(byte)
  Bar(word(3)); // Promote word to integer and use Bar(integer)

  Bar(42); // Should probably fail with ambiguity error even though 42 fits in a byte
end;

 

Share this post


Link to post
15 hours ago, Anders Melander said:

Resolve to the overload that best match the parameter?

Isn't that what it does now?

Share this post


Link to post
1 hour ago, PeterBelow said:

Isn't that what it does now?

Yes, to a degree. Bar(42) is ambiguous but resolves to byte. An int64 is demoted silently and resolves to Bar(integer). It could at least produce a warning.

 

My point was that I think it's possible to get rid of the ambiguity without breaking everything. IMO a little (compile time) breakage would be okay as long as there's a benefit.

Share this post


Link to post
On 6/11/2022 at 5:20 PM, PeterBelow said:

... 'x' can be interpreted either as a character constant or a string constant, and the compiler decides which it should be dependent on the context. A char is also assignment compatible with a variable or parameter of type string (but not vice versa).

I see no way to get around this, and changing these rules will never happen since they make sense and changing them would break heaps of older code.

Yes, probably thats true and we would end here.

 

Just to think a little out-of-the-box, towards possible Delphi future implementations:

What would happen if Delphi would add a  {$STRICT_CHAR ON/OFF} directive, which should not be too difficult IMHO ?

Could that help to solve such issues between char/string, by simply defining this when needed in the unit with the overload ?

 

I'm afraid not so easy, because the ambiguity happens in the caller, not necessarily in the overload unit itself.

So the caller unit has to use this "strict_char" around such critical parts, similar like other directives do.

Anyway, if that is good or bad or creates much other problems I cannot tell from my guts, but at least its maybe something weird to think about 🙂

 

Ok, for me the question is answered already here, since seems no new elegant workaround available now.

Thanks for your thoughts and feedback.

 

 

Share this post


Link to post
On 6/12/2022 at 3:11 PM, Anders Melander said:

Yes, to a degree. Bar(42) is ambiguous but resolves to byte. An int64 is demoted silently and resolves to Bar(integer). It could at least produce a warning.

 

My point was that I think it's possible to get rid of the ambiguity without breaking everything. IMO a little (compile time) breakage would be okay as long as there's a benefit.

Fun fact: it does with {$WARN IMPLICIT_CONVERSION_LOSS ON} (which is not turned on by default - probably because it would produce a ton of those in existing code)

Share this post


Link to post
6 minutes ago, Stefan Glienke said:

Fun fact: it does with {$WARN IMPLICIT_CONVERSION_LOSS ON} (which is not turned on by default - probably because it would produce a ton of those in existing code)

Ah! - and there's even a IMPLICIT_INTEGER_CAST_LOSS to go along with it.

Am I correct in observing that that these hasn't been surfaced in the IDE options dialog?

Share this post


Link to post
8 minutes ago, Anders Melander said:

Am I correct in observing that that these hasn't been surfaced in the IDE options dialog?

No.

image.thumb.png.0d9afe2aa8ccd988e1b1b769fa54d235.png

 

Share this post


Link to post
1 hour ago, Stefan Glienke said:

No.

Huh? Your profile says 10.1 and AFAIK they were introduced in 10.2.

Here's what I have on Delphi 11.0 (same with 10.3):

image.thumb.png.1e225cdb58d182bba6af3e84189314a5.png

Share this post


Link to post
Posted (edited)

My profile shows what I am using in production at work.

That does not mean I don't also have access to 11.1 which had this issue addressed 😉

 

Edited by Stefan Glienke

Share this post


Link to post
1 minute ago, Stefan Glienke said:

That does not mean I don't also have access to 11.1

Okay. So in other words: They're available through the UI from 11.1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×