Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2019-06

Overloading standard functions

9 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Виктор Матузенко

Hi!

I am writing my own string type and I want to overload standard string functions.
I don't want to hide them, just overload.

Consider the following example (I use 3.0.4 [2017/10/06] for i386):

=======================================
{$MODE FPC}
type
TMyString = record
end;
function Pos(const Sub, S: TMyString): LongInt;
begin
  Pos := 0;
end;
function Length(const MyString: TMyString): SizeUInt;
begin
  Length := 0;
end;
function Copy(const MyString: TMyString; A, B: LongInt): TMyString;
begin
  Copy := MyString;
end;
begin
  // Works just fine
  Writeln(Pos('a', 'abcdef'));
  // Error: Incompatible type for arg no. 1: Got "Constant String",
expected "TMyString"
  Writeln(Copy('abcdef', 1, 4));
  // Error: Incompatible type for arg no. 1: Got "Constant String",
expected "TMyString
  Writeln(Length('abcdef'));
end.
=======================================

Pos behaves differently than Length and Copy.
Seems inconsistent -- should it be considered as a bug?

Victor Matuzenko (Виктор Матузенко)

Reply by email

Marco van de Voort
to Виктор Матузенко

Op 2019-06-19 om 13:46 schreef Виктор Матузенко:

Pos behaves differently than Length and Copy. Seems inconsistent -- should it be considered as a bug?

Afaik:

- Copy and length are builtins, pos is a function. builtin+function is not "true" overloading since both are not the same kind.

- And functions should get the overload; directive for cross unit overloading

Reply by email

Виктор Матузенко
to Marco van de Voort

Thanks for the answer!

ср, 19 июн. 2019 г. в 15:07, Marco van de Voort :

Show quoted text 5 lines
- Copy and length are builtins, pos is a function. builtin+function is not "true" overloading since both are not the same kind.

Is the term "builtin" documented somewhere? Where can I find list of all builtin identifiers to check if my libraries conflict with them?

Can I overload the Pos and not be afraid of that in future versions of fpc it may suddenly become "builtin" and break some code?

Victor Matuzenko (Виктор Матузенко)

Reply by email

Marco van de Voort
to Виктор Матузенко

Op 2019-06-19 om 14:30 schreef Виктор Матузенко:

Show quoted text 13 lines
Thanks for the answer! ср, 19 июн. 2019 г. в 15:07, Marco van de Voort < >: - Copy and length are builtins, pos is a function. builtin+function is not "true" overloading since both are not the same kind. Is the term "builtin" documented somewhere? Where can I find list of all builtin identifiers to check if my libraries conflict with them?

If the documentation lists the function as being in "system.fpd" that is probably it. You can look at that file (rtl/inc in the sources) to get an idea.

Can I overload the Pos and not be afraid of that in future versions of fpc it may suddenly become "builtin" and break some code?

First, where would be the breakage? Now it works with and without overload?

Second, while the chances are low, there are no guarantees. Avoid overly complex layering of the same symbol if you are afraid of this.

Reply by email

Виктор Матузенко
to Marco van de Voort

ср, 19 июн. 2019 г. в 15:34, Marco van de Voort :

Show quoted text 5 lines
If the documentation lists the function as being in "system.fpd" that is probably it. You can look at that file (rtl/inc in the sources) to get an idea.

Ok, thanks.

First, where would be the breakage? Now it works with and without overload?

If I write a unit that overloads the Pos function and code that uses the unit
and calls standard version of Pos without namespace prefix, than this code will be broken if Pos becomes builtin.

(It just will not compile the code and the error "Incompatible type" is not clear
report the real problem.)

Now the cross unit overload does work for functions declared with the "overload"
keyword and does not without.

Second, while the chances are low, there are no guarantees. Avoid overly complex layering of the same symbol if you are afraid of this.

I am not a gambler, I ask what is guaranteed.

The Pos is just an example. I can change it to any other symbol: Foo, Default, Writeln, GetHash, DoSomething, StrToUtf8 etc. My general question is: as a library developer which symbols can I declare to be sure that user of the library will not face conflicts with standard declarations, FPC RTL etc (and how to do the declarations properly)?

The answer I see: none because any identifier may become "builtin", start to hardly conflict with any declarations with the same name and become PITA for code maintainer.

Pity.

Victor Matuzenko (Виктор Матузенко)

Reply by email

Marco van de Voort
to Виктор Матузенко

Op 2019-06-19 om 15:55 schreef Виктор Матузенко:

Show quoted text 12 lines
First, where would be the breakage? Now it works with and without overload? If I write a unit that overloads the Pos function and code that uses the unit and calls standard version of Pos without namespace prefix, than this code will be broken if Pos becomes builtin. (It just will not compile the code and the error "Incompatible type" is not clear report the real problem.)

Possibly, yes.

Now the cross unit overload does work for functions declared with the "overload" keyword and does not without.

Yes. Partially to signal you are aware that there is a prior function, and you want to put it "next" to it for overloading rather than override it.

Show quoted text 15 lines
Second, while the chances are low, there are no guarantees. Avoid overly complex layering of the same symbol if you are afraid of this. I am not a gambler, I ask what is guaranteed. The Pos is just an example. I can change it to any other symbol: Foo, Default, Writeln, GetHash, DoSomething, StrToUtf8 etc. My general question is: as a library developer which symbols can I declare to be sure that user of the library will not face conflicts with standard declarations, FPC RTL etc (and how to do the declarations properly)?

No. Such guarantees/constraints on a long term basis would paralyse the project too much. We try to avoid breaking backwards compatibility as much as possible, but we can't always guarantee it absolutely.

Over very long periods you might need to occasionally make changes to your sources. Just like e.g. Delphi

Since built-ins require compiler modification, you are however pretty safe during a compiler major cycle, since the compiler modifications merged are mostly fixes, and rarily dialectal changes.

Overriding identifiers used in system/sysutils/classes is asking for trouble. Something like POS() is in system since forever, so simply don't do that if you want to be future proof.

Show quoted text 5 lines
The answer I see: none because any identifier may become "builtin", start to hardly conflict with any declarations with the same name and become PITA for code maintainer.

No, since the default is that the local declaration overrides the system definition.

So only if you overload an RTL procedure, and then that RTL procedure is changed to builtin, you get in to problems.

The number of builtins is low, and all recent ones were not RTL procedures before. At least not general ones like POS().

So IMHO this goes way to far into the realm of the hypothetical, since I can't remember an actual case when it happened.

Reply by email

Виктор Матузенко
to Marco van de Voort

ср, 19 июн. 2019 г. в 17:10, Marco van de Voort :

Show quoted text 6 lines
No. Such guarantees/constraints on a long term basis would paralyse the project too much. We try to avoid breaking backwards compatibility as much as possible, but we can't always guarantee it absolutely.

Ok, just for clarification: is that "builtin" pitfall required at all? What makes
Length and Copy special and non-overloadable?

Victor Matuzenko (Виктор Матузенко)

Reply by email

Sven Barth
to Marco van de Voort: Op 2019-06-19 om 15:55 schreef Виктор Матузенко: Possibly, yes. Yes.…

Marco van de Voort schrieb am Mi., 19. Juni
2019, 16:09:

Show quoted text 17 lines
> The answer I see: none because any identifier may become "builtin", > start to hardly conflict with any declarations with the same name and > become PITA for code maintainer. > No, since the default is that the local declaration overrides the system definition. So only if you overload an RTL procedure, and then that RTL procedure is changed to builtin, you get in to problems. The number of builtins is low, and all recent ones were not RTL procedures before. At least not general ones like POS(). So IMHO this goes way to far into the realm of the hypothetical, since I can't remember an actual case when it happened.

3.2 changes Insert and Delete into intrinisics due to support for dynamic arrays.

Regards,
Sven

Reply by email

Sven Barth
to Виктор Матузенко: ср, 19 июн. 2019 г. в 17:10, Marco van de Voort : Ok, just for clarification: is…

Виктор Матузенко schrieb am Mi., 19. Juni 2019, 16:25:

Show quoted text 15 lines
ср, 19 июн. 2019 г. в 17:10, Marco van de Voort < >: > > No. Such guarantees/constraints on a long term basis would paralyse the > project too much. We try to avoid breaking backwards compatibility as > much as possible, but we can't always guarantee it absolutely. > > Ok, just for clarification: is that "builtin" pitfall required at all? What makes Length and Copy special and non-overloadable?

They are special because they can also be used with any array type. This isn't something that can be modeled using normal functions. And all intrinisics behave that way (e.g. Writeln does as well).

Regards,
Sven

Reply by email