Sign in Join

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

Mailing lists / fpc-devel / 2023-02

Question on constref

41 messages · 10 people · –

Reply by email Subscribe to this list Your subscription

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

Bart

Hi,

I have a function that has a parameter to a datastructure with some arrays with records is it.
This function needs to return a pointer to a specific record inside that structure.
The function is not supposed to alter the contents of this datastructure. So, I need to pass this structure by reference.
(I've spent hours and hours looking at unexpected results in my program because I passed the datastructure by value, so the returned pointer was in fact garbage...)

I thought that constref would be OK for that (the word constref suggests to me tah the paramter will be treated (by me) to be a constant, and that it shall be passed by reference in all cases, whereas with a const parameter the compiler decides upon the best way to pass it: by value or by reference).
I tried to find documentation for constref, but all I could find was: https://wiki.freepascal.org/FPC_New_Features_2.6.0#Constref_parameter_modifier There it says:
"Note that in general, it should only be used for interfacing with external code or when writing assembler routines."

I therefore decided to make it a var parameter, but that feels a bit odd since the function shall not ever alter the datastructure itself. Hence my question: is it OK to use constref for a parameter (and is it guaranteed to work by design this way) if I need to pas a parameter by reference and the function/procedure shall not modify that parameter?

(B.t.w.: Where can I find the official documentation on constref?)

Bart

Reply by email

Adriaan van Os
to Bart
Bart wrote
"Note that in general, it should only be used for interfacing with external code or when writing assembler routines."

That is wrong. It should read

"A const parameter is be passed by reference or (for small-sized parameters) by value, whatever is most efficient. A constref parameter is guaranteed to be passed by reference in all cases. The latter is therefore typically used for interfacing with external code or when writing assembler routines."

In your case, constref is the right choice, although const would be fine also (as your datastructure is larger than a small number of bytes).

Regards,

Adriaan van Os

Reply by email

Steve Litt
to Bart: I have a function that has a parameter to a datastructure with some arrays with records…

Bart via fpc-devel said on Wed, 1 Feb 2023 11:30:37 +0100

Show quoted text 8 lines
Hi, The function is not supposed to alter the contents of this datastructure. So, I need to pass this structure by reference. (I've spent hours and hours looking at unexpected results in my program because I passed the datastructure by value, so the returned pointer was in fact garbage...)

I haven't done much Pascal programming this century, but in my C programming, regardless of the way the structure is passed in, the returned pointer shouldn't be garbage unless either:

* You're returning a local pointer that goes out of scope outside the function, or,

* There's an error inside your function.

You could deduce which by looking at the contents of the pointer just before your function ends. If it's garbage, your function has an error. If not, check to see if you're returning a pointer that's a local variable, because in C at least, doing so causes problems: Often intermittent problems.

SteveT

Steve Litt
Autumn 2022 featured book: Thriving in Tough Times http://www.troubleshooters.com/bookstore/thrive.htm

Reply by email

Bart
to Adriaan van Os: That is wrong. It should read "A const parameter is be passed by reference or (for…
AM Adriaan van Os wrote 10 lines
That is wrong. It should read "A const parameter is be passed by reference or (for small-sized parameters) by value, whatever is most efficient. A constref parameter is guaranteed to be passed by reference in all cases. The latter is therefore typically used for interfacing with external code or when writing assembler routines." In your case, constref is the right choice, although const would be fine also (as your datastructure is larger than a small number of bytes).

Thank you.

Bart

Reply by email

Hairy Pixels
to Adriaan van Os: That is wrong. It should read "A const parameter is be passed by reference or (for…
On Feb 1, 2023, at 5:56 PM, Adriaan van Os via fpc-devel wrote: "A const parameter is be passed by reference or (for small-sized parameters) by value, whatever is most efficient. A constref parameter is guaranteed to be passed by reference in all cases. The latter is therefore typically used for interfacing with external code or when writing assembler routines."

I’ve been confused by constref versus const. I use constref when passing large records as parameters so I know it will not make needless copies but why doesn’t const function this way too? If the parameter is constant why would it ever make sense for the compiler to copy the entire record when it knows the function can’t change it?

Regards,
Ryan Joseph

Reply by email

Adriaan van Os
to Hairy Pixels
Hairy Pixels wrote 6 lines
> On Feb 1, 2023, at 5:56 PM, Adriaan van Os via fpc-devel wrote: > > "A const parameter is be passed by reference or (for small-sized parameters) by value, whatever is most efficient. A constref parameter is guaranteed to be passed by reference in all cases. The latter is therefore typically used for interfacing with external code or when writing assembler routines." I’ve been confused by constref versus const. I use constref when passing large records as parameters so I know it will not make needless copies but why doesn’t const function this way too? If the parameter is constant why would it ever make sense for the compiler to copy the entire record when it knows the function can’t change it?

Because, if e.g. the byte-size of a parameter is such that it fits into a CPU register, then passing the parameter itself is more efficient than passing a reference to it. For large byte-size parameters, const and constref function the same. The difference is with small byte-size parameters.

Regards,

Adriaan van Os

Reply by email

Hairy Pixels
to Adriaan van Os
On Feb 1, 2023, at 8:16 PM, Adriaan van Os via fpc-devel wrote: Because, if e.g. the byte-size of a parameter is such that it fits into a CPU register, then passing the parameter itself is more efficient than passing a reference to it. For large byte-size parameters, const and constref function the same. The difference is with small byte-size parameters.

Hmmm I was told otherwise by one of the compiler devs and that I should use constref if I want to guarantee it will not be copied. Maybe one of them can confirm this….

I tried to use https://godbolt.org/ but my program doesn’t seem to be producing any output...

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 7 lines
> On Feb 1, 2023, at 8:16 PM, Adriaan van Os via fpc-devel wrote: > > Because, if e.g. the byte-size of a parameter is such that it fits into a CPU register, then passing the parameter itself is more efficient than passing a reference to it. For large byte-size parameters, const and constref function the same. The difference is with small byte-size parameters. Hmmm I was told otherwise by one of the compiler devs and that I should use constref if I want to guarantee it will not be copied. Maybe one of them can confirm this….

That's exactly what Adriaan is saying. With const the compiler can choose. With constref, you force it not to copy. But this is not so efficient for small parameter sizes.

Michael.

Reply by email

Hairy Pixels
to Michael Van Canneyt
Show quoted text 5 lines
On Feb 1, 2023, at 8:27 PM, Michael Van Canneyt via fpc-devel wrote: That's exactly what Adriaan is saying. With const the compiler can choose. With constref, you force it not to copy. But this is not so efficient for small parameter sizes.

So const will always pass records that are over a certain size by references? That’s good to know since “const” is more pleasant to look at it than constref. :)

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 11 lines
> On Feb 1, 2023, at 8:27 PM, Michael Van Canneyt via fpc-devel wrote: > > That's exactly what Adriaan is saying. With const the compiler can choose. > With constref, you force it not to copy. But this is not so efficient for > small parameter sizes. So const will always pass records that are over a certain size by references? That’s good to know since “const” is more pleasant to look at it than constref. :)

As I wrote: The compiler can choose.
Whether it will always do what you think it should do is another matter.

It's not for no reason that constref was introduced: to leave the compiler no choice...

Michael.

Reply by email

Tomas Hajny
to Hairy Pixels: So const will always pass records that are over a certain size by references? That’s…
Hairy Pixels wrote 12 lines
> On Feb 1, 2023, at 8:27 PM, Michael Van Canneyt via fpc-devel > wrote: > > That's exactly what Adriaan is saying. With const the compiler can > choose. > With constref, you force it not to copy. But this is not so efficient > for > small parameter sizes. So const will always pass records that are over a certain size by references? That’s good to know since “const” is more pleasant to look at it than constref. :)

As written above, the compiler can choose with const (and the choice may differ for different CPUs and other factors), i.e. you shouldn't make assumptions / rely on const of particular size to be passed by reference.

Tomas

Reply by email

Sven Barth
to Hairy Pixels: So const will always pass records that are over a certain size by references? That’s…
Hairy Pixels wrote 7 lines
> On Feb 1, 2023, at 8:27 PM, Michael Van Canneyt via fpc-devel wrote: > > That's exactly what Adriaan is saying. With const the compiler can choose. > With constref, you force it not to copy. But this is not so efficient for > small parameter sizes. So const will always pass records that are over a certain size by references? That’s good to know since “const” is more pleasant to look at it than constref. :)

Which types are passed by-value or by-reference when using const is determined by the size of the record and the types of the fields based on whatever the corresponding ABI defines (e.g. the x86_64 Sys V ABI is rather explicit about some field combinations). The compiler will however not switch between passing a specific type once by-value and another time by-reference.

Regards,
Sven

Reply by email

Sven Barth
to Bart: I have a function that has a parameter to a datastructure with some arrays with records…
Bart wrote 10 lines
I thought that constref would be OK for that (the word constref suggests to me tah the paramter will be treated (by me) to be a constant, and that it shall be passed by reference in all cases, whereas with a const parameter the compiler decides upon the best way to pass it: by value or by reference). I tried to find documentation for constref, but all I could find was: https://wiki.freepascal.org/FPC_New_Features_2.6.0#Constref_parameter_modifier There it says: "Note that in general, it should only be used for interfacing with external code or when writing assembler routines."

“constref” is guaranteed to pass the parameter by reference. And the compiler will ensure that it can't be modified as reasonably possible (as with “const” there are ways to circumvent this, e.g. by passing around a pointer to the parameter, but the general cases are covered).

(B.t.w.: Where can I find the official documentation on constref?)

There is no full documentation for that parameter modifier (someone might want to file a bug report for that), but the documentation for “const” (
https://www.freepascal.org/docs-html/current/ref/refsu67.html#x183-20700014.4.4 ) contains this:

=== doc begin ===

Contrary to Delphi, no assumptions should be made about how const parameters are passed to the underlying routine. In particular, the assumption that parameters with large size are passed by reference is not correct. For this the constref parameter type should be used, which is available as of version 2.5.1 of the compiler.

=== doc end ===

Regards,
Sven

Reply by email

Michael Van Canneyt
to Sven Barth
Sven Barth wrote 22 lines
Am 01.02.2023 um 11:30 schrieb Bart via fpc-devel: > I thought that constref would be OK for that (the word constref > suggests to me tah the paramter will be treated (by me) to be a > constant, and that it shall be passed by reference in all cases, > whereas with a const parameter the compiler decides upon the best way > to pass it: by value or by reference). > I tried to find documentation for constref, but all I could find was: > https://wiki.freepascal.org/FPC_New_Features_2.6.0#Constref_parameter_modifier > There it says: > "Note that in general, it should only be used for interfacing with > external code or when writing assembler routines." “constref” is guaranteed to pass the parameter by reference. And the compiler will ensure that it can't be modified as reasonably possible (as with “const” there are ways to circumvent this, e.g. by passing around a pointer to the parameter, but the general cases are covered). > (B.t.w.: Where can I find the official documentation on constref?) There is no full documentation for that parameter modifier (someone might want to file a bug report for that), but the documentation for “const” ( https://www.freepascal.org/docs-html/current/ref/refsu67.html#x183-20700014.4.4 ) contains this:

No need for a bugreport, this already is changed in trunk docs:

https://www.freepascal.org/daily/doc/ref/refsu68.html#x184-20800014.4.4

Michael.

Reply by email

Hairy Pixels
to Sven Barth: Which types are passed by-value or by-reference when using const is determined by the…
On Feb 2, 2023, at 4:38 AM, Sven Barth wrote: Which types are passed by-value or by-reference when using const is determined by the size of the record and the types of the fields based on whatever the corresponding ABI defines (e.g. the x86_64 Sys V ABI is rather explicit about some field combinations). The compiler will however not switch between passing a specific type once by-value and another time by-reference.

So if the compiler is making the choice using const which is more efficient then we should be using const always I would think? What problem does constref solve then?

You go on to show this:

=== doc begin ===

Contrary to Delphi, no assumptions should be made about how const parameters are passed to the underlying routine. In particular, the assumption that parameters with large size are passed by reference is not correct. For this the constref parameter type should be used, which is available as of version 2.5.1 of the compiler.

=== doc end ===

Is there anytime a large record should be passed by value if the function is not altering the it? This is why I started using constref, i.e. "In particular, the assumption that parameters with large size are passed by reference is not correct” which makes me think if I have a record of say 64k if I don’t use constref it could be passed by value to a function which is called in a tight loop.

Regards,
Ryan Joseph

Reply by email

Adriaan van Os
to Sven Barth: “constref” is guaranteed to pass the parameter by reference. And the compiler will…
Sven Barth wrote 15 lines
There is no full documentation for that parameter modifier (someone might want to file a bug report for that), but the documentation for “const” ( https://www.freepascal.org/docs-html/current/ref/refsu67.html#x183-20700014.4.4 ) contains this: === doc begin === Contrary to Delphi, no assumptions should be made about how const parameters are passed to the underlying routine. In particular, the assumption that parameters with large size are passed by reference is not correct. For this the constref parameter type should be used, which is available as of version 2.5.1 of the compiler. === doc end ===

On debate, see FPC issue 17442.

Regards,

Adriaan van Os

Reply by email

Adriaan van Os
to Hairy Pixels: So if the compiler is making the choice using const which is more efficient then we…
Hairy Pixels wrote 16 lines
> On Feb 2, 2023, at 4:38 AM, Sven Barth wrote: > > Which types are passed by-value or by-reference when using const is determined by the size of the record and the types of the fields based on whatever the corresponding ABI defines (e.g. the x86_64 Sys V ABI is rather explicit about some field combinations). The compiler will however not switch between passing a specific type once by-value and another time by-reference. So if the compiler is making the choice using const which is more efficient then we should be using const always I would think? What problem does constref solve then? You go on to show this: === doc begin === Contrary to Delphi, no assumptions should be made about how const parameters are passed to the underlying routine. In particular, the assumption that parameters with large size are passed by reference is not correct. For this the constref parameter type should be used, which is available as of version 2.5.1 of the compiler. === doc end === Is there anytime a large record should be passed by value if the function is not altering the it? This is why I started using constref, i.e. "In particular, the assumption that parameters with large size are passed by reference is not correct” which makes me think if I have a record of say 64k if I don’t use constref it could be passed by value to a function which is called in a tight loop.

Yes, to me this notion in the docs lacks all logic. How could copying large parameters ever be efficient ? It invites developers to use constref for a purpose it wasn't meant for. For a debate, see FPC Issue 17442. Jonas Maebe writes there

"We are adding support for a "constref" parameter to the compiler that will allow you to specify that a parameter is const and must be passed by reference, but I would strongly suggest to use it only for compatibility with external APIs (that e.g. use "const struct *" parameters) and very sparingly for optimization, because in many cases you may well slow down your code rather than speeding it up."

but also

"Maybe another developer believes that for non-cdecl, we could modify the behaviour of const so that very large records are passed by reference, which is why I leave this bug open."

If my memory is correct, that this was changed for macpas mode (passing very large records by reference) but can't find it back in the bug database.

Regards,

Adraan van Os

Reply by email

Hairy Pixels
to Adriaan van Os: On debate, see FPC issue 17442. Adriaan van Os
On Feb 2, 2023, at 8:41 AM, Adriaan van Os via fpc-devel wrote: On debate, see FPC issue 17442.

https://gitlab.com/freepascal.org/fpc/source/-/issues/17442

Yes this is what I remember learning.

What I think should be documented is what you should do for records that over a certain size. In my old code I have some 64k records and I starting using constref with but would using “const” be sufficient to pass by reference also?

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Hairy Pixels: So if the compiler is making the choice using const which is more efficient then we…
Hairy Pixels wrote 5 lines
> On Feb 2, 2023, at 4:38 AM, Sven Barth wrote: > > Which types are passed by-value or by-reference when using const is determined by the size of the record and the types of the fields based on whatever the corresponding ABI defines (e.g. the x86_64 Sys V ABI is rather explicit about some field combinations). The compiler will however not switch between passing a specific type once by-value and another time by-reference. So if the compiler is making the choice using const which is more efficient then we should be using const always I would think? What problem does constref solve then?

The case when you *need* a constant reference. Case in point: the passing of TGuid in IInterface.QueryInterface. Delphi code relies on it being a reference, but “const” does not guarantee that for all platforms.

Show quoted text 10 lines
You go on to show this: === doc begin === Contrary to Delphi, no assumptions should be made about how const parameters are passed to the underlying routine. In particular, the assumption that parameters with large size are passed by reference is not correct. For this the constref parameter type should be used, which is available as of version 2.5.1 of the compiler. === doc end === Is there anytime a large record should be passed by value if the function is not altering the it? This is why I started using constref, i.e. "In particular, the assumption that parameters with large size are passed by reference is not correct” which makes me think if I have a record of say 64k if I don’t use constref it could be passed by value to a function which is called in a tight loop.

I doubt that records larger than say 4 register widths are ever passed by-value. But I definitely can't exclude it, cause I don't know every ABI of every platform by heart. So if you want details there then study the ABIs of the platforms you use.

Regards,
Sven

Reply by email

Sven Barth
to Adriaan van Os: On debate, see FPC issue 17442. Adriaan van Os

Adriaan van Os via fpc-devel schrieb am
Do., 2. Feb. 2023, 02:47:

Show quoted text 21 lines
Sven Barth via fpc-devel wrote: > There is no full documentation for that parameter modifier (someone > might want to file a bug report for that), but the documentation for > “const” ( > https://www.freepascal.org/docs-html/current/ref/refsu67.html#x183-20700014.4.4 > ) contains this: > > === doc begin === > > Contrary to Delphi, no assumptions should be made about how const > parameters are passed to the underlying routine. In particular, the > assumption that parameters with large size are passed by reference is > not correct. For this the constref parameter type should be used, which > is available as of version 2.5.1 of the compiler. > > === doc end === On debate, see FPC issue 17442.

There is no debate planned for this in the near future. So even if this might change some time in the future it does not *now* and very likely also not for 3.4.0. Not to mention that things wouldn't change for most platforms anyway, because they only have the platform ABI available and that *must not* change for compatibility with C code.

Regards,
Sven

Reply by email

Adriaan van Os
to Sven Barth: The case when you *need* a constant reference. Case in point: the passing of TGuid in…
Sven Barth wrote
I doubt that records larger than say 4 register widths are ever passed by-value. But I definitely can't exclude it, cause I don't know every ABI of every platform by heart. So if you want details there then study the ABIs of the platforms you use.

I doubt that any ABI would say something about Pascal "const" rather than C "const". So, that argument doesn't hold. The only reason I can possibly think of (for "const" to pass large parameters by value and thus violating the rule that "const" chooses the most efficient method) is compatibility with Delphi in delphi compiler mode. If that's true, which I don't know, it should be documented. Otherwise, "const" will continue to puzzle programmers - and in a year we will have the same discussion again.

Regards,

Adriaan van Os

Reply by email

Michael Van Canneyt
to Sven Barth: The case when you *need* a constant reference. Case in point: the passing of TGuid in…
Sven Barth wrote 17 lines
Am 02.02.2023 um 02:09 schrieb Hairy Pixels: > >> On Feb 2, 2023, at 4:38 AM, Sven Barth wrote: >> >> Which types are passed by-value or by-reference when using const is determined by the size of the record and the types of the fields based on whatever the corresponding ABI defines (e.g. the x86_64 Sys V ABI is rather explicit about some field combinations). The compiler will however not switch between passing a specific type once by-value and another time by-reference. > So if the compiler is making the choice using const which is more efficient then we should be using const always I would think? What problem does constref solve then? The case when you *need* a constant reference. Case in point: the passing of TGuid in IInterface.QueryInterface. Delphi code relies on it being a reference, but “const” does not guarantee that for all platforms.

Exactly. That is why they introduced [ref], which serves essentially the same purpose as constref.

https://docwiki.embarcadero.com/RADStudio/Sydney/en/Parameters_(Delphi)#Constant_Parameters

We were way ahead of them on that particular one.
Benefits of being cross-platform since a long time :-)

Michael.

Reply by email

Adriaan van Os
to Sven Barth: Adriaan van Os via fpc-devel schrieb am Do., 2. Feb. 2023, 02:47: There is no debate…
Sven Barth wrote 34 lines
Adriaan van Os via fpc-devel < > schrieb am Do., 2. Feb. 2023, 02:47: Sven Barth via fpc-devel wrote: > There is no full documentation for that parameter modifier (someone > might want to file a bug report for that), but the documentation for > “const” ( > https://www.freepascal.org/docs-html/current/ref/refsu67.html#x183-20700014.4.4 > ) contains this: > > === doc begin === > > Contrary to Delphi, no assumptions should be made about how const > parameters are passed to the underlying routine. In particular, the > assumption that parameters with large size are passed by reference is > not correct. For this the constref parameter type should be used, which > is available as of version 2.5.1 of the compiler. > > === doc end === On debate, see FPC issue 17442. There is no debate planned for this in the near future. So even if this might change some time in the future it does not *now* and very likely also not for 3.4.0. Not to mention that things wouldn't change for most platforms anyway, because they only have the platform ABI available and that *must not* change for compatibility with C code.

"Will not change" and "ABI" are non arguments. They do not address the questions put forward here:

- under what circumstances (and in what compiler mode) does FPC pass large (say 1 MB or 1 GB) "const" parameters by value (which is extremely inefficient) ?
- for what reasons ?

As long as these questions are not addressed and cleared and documented "const" parameters are not a useable in actual code.

Regards,

Adriaan van Os

Reply by email

Ondrej Pokorny
to Sven Barth: The case when you *need* a constant reference. Case in point: the passing of TGuid in…
Sven Barth wrote
The case when you *need* a constant reference. Case in point: the passing of TGuid in IInterface.QueryInterface. Delphi code relies on it being a reference, but “const” does not guarantee that for all platforms.

Maybe I am missing something, could you please explain why IInterface.QueryInterface needs constref?

Delphi definition doesn't use it (Delphi 11):

   IInterface = interface
     ['{00000000-0000-0000-C000-000000000046}']
     function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
     function _AddRef: Integer; stdcall;
     function _Release: Integer; stdcall;
   end;

FPC uses it in the definition:

       IUnknown = interface
         ['{00000000-0000-0000-C000-000000000046}']          function QueryInterface({$IFDEF
FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};          function _AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
         function _Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
       end;

but when I check all the implementations of QueryInterface in objpas.inc I see that the iid is paased to TObject.QueryInterface

       function TObject.GetInterface(const iid : tguid;out obj) : boolean;

that has a const without ref.

---

I myself cannot think of any real use case of constref other than hacks like the FreeAndNil in recent Delphi versions:

procedure FreeAndNil(const [ref] Obj: TObject);

that needs the ref because it changes it to nil (so is not really a const - it used to be an untyped var). As a result FreeAndNil allows you to happily pass read-only properties to it that are then nilled.

Maybe somebody knows a real use case?

Ondrej

Reply by email

Hairy Pixels
to Adriaan van Os: "Will not change" and "ABI" are non arguments. They do not address the questions put…
Show quoted text 6 lines
On Feb 2, 2023, at 3:57 PM, Adriaan van Os via fpc-devel wrote: - under what circumstances (and in what compiler mode) does FPC pass large (say 1 MB or 1 GB) "const" parameters by value (which is extremely inefficient) ? - for what reasons ? As long as these questions are not addressed and cleared and documented "const" parameters are not a useable in actual code.

That’s what I want to know. There should be a hard rule like records over X bytes are passed by reference. In the issue you posted they said they had a massive performance penalty in their library because const was passing by value and this makes people like myself paranoid so I’m inclined to use constref everywhere with larger records.

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ondrej Pokorny: Maybe I am missing something, could you please explain why IInterface.QueryInterface…
Ondrej Pokorny wrote 8 lines
On 02.02.2023 07:42, Sven Barth via fpc-devel wrote: > The case when you *need* a constant reference. Case in point: the > passing of TGuid in IInterface.QueryInterface. Delphi code relies on > it being a reference, but “const” does not guarantee that for all > platforms. Maybe I am missing something, could you please explain why IInterface.QueryInterface needs constref?

IIRC Joost introduced it at some point, but I don't remember exactly why.

I seem to remember constref was introduced as the equivalent of const * parameters in C. It allows to skip the pointer.

Show quoted text 14 lines
      function TObject.GetInterface(const iid : tguid;out obj) : boolean; that has a const without ref. --- I myself cannot think of any real use case of constref other than hacks like the FreeAndNil in recent Delphi versions: procedure FreeAndNil(const [ref] Obj: TObject); that needs the ref because it changes it to nil (so is not really a const - it used to be an untyped var). As a result FreeAndNil allows you to happily pass read-only properties to it that are then nilled.

Which is a clear violation of the concept of Const... :/

Michael.

Reply by email

Ondrej Pokorny
to Michael Van Canneyt
Michael Van Canneyt wrote 11 lines
On Thu, 2 Feb 2023, Ondrej Pokorny via fpc-devel wrote: > I myself cannot think of any real use case of constref other than > hacks like the FreeAndNil in recent Delphi versions: > > procedure FreeAndNil(const [ref] Obj: TObject); > > that needs the ref because it changes it to nil (so is not really a > const - it used to be an untyped var). As a result FreeAndNil allows > you to happily pass read-only properties to it that are then nilled. Which is a clear violation of the concept of Const... :/

Exactly.

Ondrej

Reply by email

Michael Van Canneyt
to Hairy Pixels: That’s what I want to know. There should be a hard rule like records over X bytes are…
Hairy Pixels wrote 14 lines
> On Feb 2, 2023, at 3:57 PM, Adriaan van Os via fpc-devel wrote: > > - under what circumstances (and in what compiler mode) does FPC pass large (say 1 MB or 1 GB) "const" parameters by value (which is extremely inefficient) ? > - for what reasons ? > > As long as these questions are not addressed and cleared and documented "const" parameters are not a useable in actual code. That’s what I want to know. There should be a hard rule like records over X bytes are passed by reference. In the issue you posted they said they had a massive performance penalty in their library because const was passing by value and this makes people like myself paranoid so I’m inclined to use constref everywhere with larger records.

It depends on the platform ABI, and hence we don't document it.

I don't see your problem, to be honest:

You want to indicate a parameter to a function cannot be changed in its implementation.

If you don't care about how it is passed, use const.

If really want a reference to be passed for performance or other reasons, use constref.

So you're doing the right thing by using constref.

Couldn't be simpler.

Michael.

Reply by email

Michael Van Canneyt
to Ondrej Pokorny: Exactly. Ondrej
Ondrej Pokorny wrote 14 lines
On 02.02.2023 10:15, Michael Van Canneyt via fpc-devel wrote: > On Thu, 2 Feb 2023, Ondrej Pokorny via fpc-devel wrote: >> I myself cannot think of any real use case of constref other than hacks >> like the FreeAndNil in recent Delphi versions: >> >> procedure FreeAndNil(const [ref] Obj: TObject); >> >> that needs the ref because it changes it to nil (so is not really a const >> - it used to be an untyped var). As a result FreeAndNil allows you to >> happily pass read-only properties to it that are then nilled. > > Which is a clear violation of the concept of Const... :/ Exactly.

Probably an ill-advised "fix" to the problem that FreeAndNil accepted an untyped var and you could basically pass anything.

Michael.

Reply by email

Ondrej Pokorny
to Michael Van Canneyt
Michael Van Canneyt wrote 20 lines
On Thu, 2 Feb 2023, Ondrej Pokorny via fpc-devel wrote: > On 02.02.2023 10:15, Michael Van Canneyt via fpc-devel wrote: >> On Thu, 2 Feb 2023, Ondrej Pokorny via fpc-devel wrote: >>> I myself cannot think of any real use case of constref other than >>> hacks like the FreeAndNil in recent Delphi versions: >>> >>> procedure FreeAndNil(const [ref] Obj: TObject); >>> >>> that needs the ref because it changes it to nil (so is not really a >>> const - it used to be an untyped var). As a result FreeAndNil >>> allows you to happily pass read-only properties to it that are then >>> nilled. >> >> Which is a clear violation of the concept of Const... :/ > > Exactly. Probably an ill-advised "fix" to the problem that FreeAndNil accepted an untyped var and you could basically pass anything.

Yes. Solving one problem by creating a different one. The best would be if the FreeAndNil declaration alternated between constref and untyped var between builds. In that case you would be able to hunt both the issues :)

Seriously, having a [loose] modifier would be much more useful, IMO:

FreeAndNil(var [loose] Obj: TObject);

That would allow you to pass any TObject descendant.

Ondrej

Reply by email

Michael Van Canneyt
to Ondrej Pokorny
Ondrej Pokorny wrote 30 lines
On 02.02.2023 10:22, Michael Van Canneyt via fpc-devel wrote: > On Thu, 2 Feb 2023, Ondrej Pokorny via fpc-devel wrote: >> On 02.02.2023 10:15, Michael Van Canneyt via fpc-devel wrote: >>> On Thu, 2 Feb 2023, Ondrej Pokorny via fpc-devel wrote: >>>> I myself cannot think of any real use case of constref other than hacks >>>> like the FreeAndNil in recent Delphi versions: >>>> >>>> procedure FreeAndNil(const [ref] Obj: TObject); >>>> >>>> that needs the ref because it changes it to nil (so is not really a >>>> const - it used to be an untyped var). As a result FreeAndNil allows you >>>> to happily pass read-only properties to it that are then nilled. >>> >>> Which is a clear violation of the concept of Const... :/ >> >> Exactly. > > Probably an ill-advised "fix" to the problem that FreeAndNil accepted an > untyped var > and you could basically pass anything. Yes. Solving one problem by creating a different one. The best would be if the FreeAndNil declaration alternated between constref and untyped var between builds. In that case you would be able to hunt both the issues :) Seriously, having a [loose] modifier would be much more useful, IMO: FreeAndNil(var [loose] Obj: TObject); That would allow you to pass any TObject descendant.

It would solve this problem, but introduces another problem, since it would be usable anywhere:

Procedure DoSomething(var [loose] Obj: TObject);
var
   a : TSomeObject;
begin
   a:=TSomeobject.Create;
   DoSomething(a);
end;

after DoSomething, A may contain a class that is not a TSomeObject at all, leading to more problems.

In userspace, the best seems

Function FreeAndNil<T :TObject>(Obj : T) : T;
begin
   Obj.Free;
   Result:=Nil;
end;
With automatic type inference for generics, this allows you to do
A:=FreeAndNil(A);

Which is quite acceptable IMO. If you could add inline to the generic definition the overhead would be minimal.

As it is, FreeAndNil() seems like another candidate for a compiler intrinsic ;-)

Michael.

Reply by email

Sven Barth
to Adriaan van Os: "Will not change" and "ABI" are non arguments. They do not address the questions put…

Adriaan van Os via fpc-devel schrieb am
Do., 2. Feb. 2023, 09:57:

Show quoted text 52 lines
Sven Barth wrote: > Adriaan van Os via fpc-devel < > > schrieb am Do., 2. Feb. 2023, > 02:47: > > Sven Barth via fpc-devel wrote: > > > There is no full documentation for that parameter modifier (someone > > might want to file a bug report for that), but the documentation for > > “const” ( > > > https://www.freepascal.org/docs-html/current/ref/refsu67.html#x183-20700014.4.4 > > > ) contains this: > > > > === doc begin === > > > > Contrary to Delphi, no assumptions should be made about how const > > parameters are passed to the underlying routine. In particular, the > > assumption that parameters with large size are passed by > reference is > > not correct. For this the constref parameter type should be used, > which > > is available as of version 2.5.1 of the compiler. > > > > === doc end === > > On debate, see FPC issue 17442. > > > There is no debate planned for this in the near future. So even if this > might change some time in the future it does not *now* and very likely > also not for 3.4.0. Not to mention that things wouldn't change for most > platforms anyway, because they only have the platform ABI available and > that *must not* change for compatibility with C code. "Will not change" and "ABI" are non arguments. They do not address the questions put forward here: - under what circumstances (and in what compiler mode) does FPC pass large (say 1 MB or 1 GB) "const" parameters by value (which is extremely inefficient) ? - for what reasons ? As long as these questions are not addressed and cleared and documented "const" parameters are not a useable in actual code.

We do not document these on purpose, because they're mainly dictated by the platform ABI (on the majority of targets FPC supports simply the C ABI is used). The main part of "const" is that you can't (easily) modify the value and that reference counting isn't done when passing the value around. Whether it's passed by-value or by-reference is irrelevant for that. If you need pass-by-reference then use those modifiers that are intended for them, namely "var", "out" and "constref".

Regards,
Sven

Reply by email

Ondrej Pokorny
to Michael Van Canneyt: It would solve this problem, but introduces another problem, since it would be usable…
Michael Van Canneyt wrote 12 lines
In userspace, the best seems Function FreeAndNil<T :TObject>(Obj : T) : T; begin   Obj.Free;   Result:=Nil; end; With automatic type inference for generics, this allows you to do A:=FreeAndNil(A);

Michael, bringing your idea to the next step, FPC can do it !!!

program test;
{$mode objfpc}{$h+}
{$modeswitch ImplicitFunctionSpecialization}
generic procedure FreeAndNil<T: TObject>(var Obj: T);
var
   Temp: TObject;
begin
   Temp := Obj;
   Obj := nil;
   Temp.Free;
end;
type
   TSomeObject = class(TObject);
var
   X: TSomeObject;
begin
   X := TSomeObject.Create;
   FreeAndNil(X);
   if Assigned(X) then
     Writeln('assigned')
   else
     Writeln('nil');
   ReadLn;
end.

-----

Delphi cannot do it. FPC rocks!

The only disadvantage is that you get a FreeAndNil copy for every type you pass into the parameter (?)

Do we want to change it in SysUtils? :)

Ondrej

Reply by email

Marco van de Voort
to Ondrej Pokorny
Ondrej Pokorny wrote
The only disadvantage is that you get a FreeAndNil copy for every type you pass into the parameter (?)

Are they actually made global ? Will two freeandnil<integers> in different units use the same?

But that is just curiousity, IMHO this remedy is worse than problem. Obviously even.

Reply by email

Mattias Gaertner
to Marco van de Voort: Are they actually made global ? Will two freeandnil<integers> in different units use the…

On Thu, 2 Feb 2023 12:19:46 +0100

Marco van de Voort wrote 7 lines
On 2-2-2023 12:00, Ondrej Pokorny via fpc-devel wrote: > > > The only disadvantage is that you get a FreeAndNil copy for every > type you pass into the parameter (?) Are they actually made global ?

yes

Will two freeandnil<integers> in different units use the same?

yes (except for packages).

Mattias

Reply by email

Ondrej Pokorny
to Mattias Gaertner: On Thu, 2 Feb 2023 12:00:31 +0100 +1 I got a feeling this solves a problem no one had.…
Mattias Gaertner wrote 12 lines
On Thu, 2 Feb 2023 12:00:31 +0100 Ondrej Pokorny via fpc-devel wrote: > [...] > Delphi cannot do it. FPC rocks! +1 > The only disadvantage is that you get a FreeAndNil copy for every > type you pass into the parameter (?) > > Do we want to change it in SysUtils? :) I got a feeling this solves a problem no one had.

I agree with that.

But you know, pascal is strongly typed. You should get a compiler warning when passing an incompatible parameter and not an AV in runtime. Like the Pos(Variant) issue :)

Ondrej

Reply by email

Ondrej Pokorny
to Marco van de Voort: Are they actually made global ? Will two freeandnil<integers> in different units use the…
Marco van de Voort wrote 8 lines
On 2-2-2023 12:00, Ondrej Pokorny via fpc-devel wrote: > The only disadvantage is that you get a FreeAndNil copy for every > type you pass into the parameter (?) Are they actually made global ? Will two freeandnil<integers> in different units use the same? But that is just curiousity, IMHO this remedy is worse than problem. Obviously even.

In case FreeAndNil is inlined, there is no problem, is there?

generic procedure FreeAndNil<T: TObject>(var Obj: T); inline;
var
   Temp: TObject;
begin
   Temp := Obj;
   Obj := nil;
   Temp.Free;
end;

I checked now and on i386-win32 the generic FreeAndNil is actually inlined:

program test;
{$mode objfpc}{$h+}
{$modeswitch ImplicitFunctionSpecialization}
generic procedure FreeAndNil<T: TObject>(var Obj: T);
var
   Temp: TObject;
begin
   Temp := Obj;
   Obj := nil;
   Temp.Free;
end;
type
   TSomeObject = class(TObject);
   TSomeObject2 = class(TObject);
var
   X: TSomeObject;
   X2: TSomeObject2;
const
   IsNil: array[Boolean] of string = ('nil', 'assigned');
begin
   X := TSomeObject.Create;
   X2 := TSomeObject2.Create;
   FreeAndNil(X);
   FreeAndNil(X2);
   Writeln('X  ', IsNil[Assigned(X)]);
   Writeln('X2 ', IsNil[Assigned(X2)]);
   ReadLn;
end.

So everything good!

Ondrej

Reply by email

Sven Barth
to Ondrej Pokorny: Maybe I am missing something, could you please explain why IInterface.QueryInterface…

Ondrej Pokorny via fpc-devel schrieb am
Do., 2. Feb. 2023, 10:06:

Show quoted text 9 lines
On 02.02.2023 07:42, Sven Barth via fpc-devel wrote: > The case when you *need* a constant reference. Case in point: the > passing of TGuid in IInterface.QueryInterface. Delphi code relies on > it being a reference, but “const” does not guarantee that for all > platforms. Maybe I am missing something, could you please explain why IInterface.QueryInterface needs constref?

This isn't about Delphi, but about Windows (and XPCOM): there the IID parameter is declared as "REFIID" which is essentially a pointer. On x86 with stdcall this is not a problem, because there "const TGUID" will be passed as reference, same on x86_64 and also ARM64. However on ARM this is *not* the case (and which is how that issue was discovered). So to avoid having to add a cast to a PGUID to every QueryInterface call we instead decided to introduce "constref" which results in fewer modifications for existing code.

Maybe somebody knows a real use case?

There are cases were you really need a reference and want it to be const. Or were you want to avoid a copy *for sure* (e.g. Embedded targets).

Only because you can't see a reason for it doesn't mean that it doesn't exist.

Regards,
Sven

Reply by email

Sven Barth
to Ondrej Pokorny: Michael, bringing your idea to the next step, FPC can do it !!! program test; {$mode…

Ondrej Pokorny via fpc-devel schrieb am
Do., 2. Feb. 2023, 12:09:

Show quoted text 7 lines
Delphi cannot do it. FPC rocks! The only disadvantage is that you get a FreeAndNil copy for every type you pass into the parameter (?) Do we want to change it in SysUtils? :)

As you might have noticed you need to add a modeswitch to enable this and this modeswitch is there for a reason (namely that this could lead to wrong code being used). So by default no one would benefit from this addition and probably not even know that it's there.

Regards,
Sven

Reply by email