Auto vars (again)

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Auto vars (again)

Ryan Joseph
I had some free time recently so I decided as a learning experience to fork the compiler and implement the “auto var” idea that was mentioned a few weeks ago.

What I found is that it’s a pretty lightweight (in terms of impact on the compiler) and unintrusive way to manage memory on a per-scope basis which solves a minor but common pattern. This was highly debated but I know from my personal experience I often know at the time of declaring a class that I will allocate the class at the top of the function and free at the end. If this was C++ I would declare the class on the stack but we don’t have that option in Pascal so we’re forced into either a class or record paradigm. The better option would probably be full blown ARC but I don’t know if that’s ever going to be on the agenda of FPC.

I understand there’s lots of potential problems like passing an auto var out of scope, classes with constructors that require parameters (rather big problem) or perhaps the “auto” modifier (I did that because it was easy to parse) but is there any merit to this idea if it was cleaned up and made safe? I did some tests to see if you could prevent passing auto vars out of scope and it’s possible to prevent most ways but not 100%.

========================================================

program auto_test_1;

type
        TMyClass = class
                data: TObject; auto;
        end;

var
        obj: TMyClass; auto;
begin
        // obj is auto so TMyClass.Create is called along with subsequent auto var members
        writeln('obj:', obj.classname);
        writeln('data:', obj.data.classname);
        // when TMyClass.Free is called auto var members call Free also
end.

program auto_test_2;
uses
        fgl;

var
        list: specialize TFPGList<integer>; auto;
        i: integer;
begin
        // list is auto so TFPGList<integer>.Create is called
        // TFPGList as an auto var is a compelling alternative to dynamic arrays
        list.Add(1);
        list.Add(2);
        list.Add(3);
        for i in list do
                writeln(i);
end.


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Maciej Izak
2018-08-18 1:24 GMT+02:00 Ryan Joseph <[hidden email]>:
but is there any merit to this idea if it was cleaned up and made safe? I did some tests to see if you could prevent passing auto vars out of scope and it’s possible to prevent most ways but not 100%.

You are trying to achieve something what is almost possible with current FPC trunk version (which is also safe in the case of exception):

=== code begin ===
  TAutoCreate<T: class> = record
    _: T;
    class operator Initialize(var a: TAutoCreate<T>);
    class operator Finalize(var a: TAutoCreate<T>);
    class operator Copy(constref a: TAutoCreate<T>; var b: TAutoCreate<T>);
    class operator AddRef(var a: TAutoCreate<T>);
  end;

class operator TAutoCreate<T>.Initialize(var a: TAutoCreate<T>);
begin
  a._ := T.Create;
end;

class operator TAutoCreate<T>.Finalize(var a: TAutoCreate<T>);
begin
  a._.Free;
end;

class operator TAutoCreate<T>.Copy(constref a: TAutoCreate<T>;
  var b: TAutoCreate<T>);
begin
  // raise { some exception - prevent copy };
end;

class operator TAutoCreate<T>.AddRef(var a: TAutoCreate<T>);
begin
  // raise { some exception - prevent copy };
end;

var
  o: TAutoCreate<TObject>;
begin // auto Create
  Writeln(o._.ToString); 
end. // auto Free;
=== code end ===

the code above was not tested (written now) but should work. The only disadvantage of above solution is lack of the way to omit "_". 

The solution which probably should be accepted by fpc team is default property without indexer:

=== code begin ===
property obj: T read fobj; default;
=== code end ===

with this property should be possible:

=== code begin ===
Writeln(o.ToString); 
=== code end ===

next you can try to provide compiler magic:

=== code begin ===
var o: TObject auto; // equivalent of var o: TAutoCreate<TObject>;
// please note that syntax with semicolon is improper: 
//var o: TObject; auto; 
=== code end ===

anyway I am not big fan of "auto" because it means many troubles, what if:

* there is no parameter less constructor
* why to call constructor when there is NewInstance (this seems more proper for this case but...)

after NewInstance you can (and rather this will be necessary) call selected constructor from instance which will be executed like regular method, but what is the sense of "auto" calling NewInstance when you still need to execute Constructor? :D

I see rather no reason for this feature in compiler (but nice try :) ). There are many other directions to solve "auto free" problems without compiler modifications - maybe not all can be solved but many problems for sure - for example you have ready to use TAutoFree from mORMot or initial experiments with ARC objects for NewPascal (still worth to mention ;P).

-- 
Best regards,
Maciej Izak

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Ryan Joseph


> On Aug 17, 2018, at 7:50 PM, Maciej Izak <[hidden email]> wrote:
>
> 2018-08-18 1:24 GMT+02:00 Ryan Joseph <[hidden email]>:
> but is there any merit to this idea if it was cleaned up and made safe? I did some tests to see if you could prevent passing auto vars out of scope and it’s possible to prevent most ways but not 100%.
>
> You are trying to achieve something what is almost possible with current FPC trunk version (which is also safe in the case of exception):
>
> var
>   o: TAutoCreate<TObject>;
> begin // auto Create
>   Writeln(o._.ToString);
> end. // auto Free;
> === code end ===
>
> the code above was not tested (written now) but should work. The only disadvantage of above solution is lack of the way to omit "_”.

I think you may be right about this. Having “auto” leverage the existing management operators makes sense. It’s more overhead to wrap everything in records though and the larger problem is the so-called syntactic sugar missing.

>
> The solution which probably should be accepted by fpc team is default property without indexer:
>
> === code begin ===
> property obj: T read fobj; default;
> === code end ===
>
> with this property should be possible:
>
> === code begin ===
> Writeln(o.ToString);
> === code end ===

How does that property work exactly? Making this work properly is the most important part but I never considered it. It sounds like “with” statements kind of.

>
> next you can try to provide compiler magic:
>
> === code begin ===
> var o: TObject auto; // equivalent of var o: TAutoCreate<TObject>;
> // please note that syntax with semicolon is improper:
> //var o: TObject; auto;
> === code end ===

I wasn’t sure what’s proper. Function modifiers look like that but there are no var modifiers in Pascal as of now. The absolute keyword is kind of similar. We have “threadvar” also which sets a precedence and starts a new section which is nice. Either way I really like the idea of introducing a keyword instead of using the generic <> syntax.

>
> anyway I am not big fan of "auto" because it means many troubles, what if:
>
> * there is no parameter less constructor

That’s the biggest problem IMO. Calling NewInstance is correct, you’re right, but if the class needs parameters you need to call the constructor again anyways so it kind of defeats the purpose. When the class has only a parameterless constructor it works beautifully though so the feature is limited to those cases.

> * why to call constructor when there is NewInstance (this seems more proper for this case but...)
>
> after NewInstance you can (and rather this will be necessary) call selected constructor from instance which will be executed like regular method, but what is the sense of "auto" calling NewInstance when you still need to execute Constructor? :D

Agreed, that limits the usefulness of the feature. When it works well it’s a real win though.

>
> I see rather no reason for this feature in compiler (but nice try :) ). There are many other directions to solve "auto free" problems without compiler modifications - maybe not all can be solved but many problems for sure - for example you have ready to use TAutoFree from mORMot or initial experiments with ARC objects for NewPascal (still worth to mention ;P).

How does TAutoFree in mORMot work? Never heard of this.

Full blown ARC is probably the best option but I was thinking of intermediate steps that are easy to implement and solve a a portion of the common use cases. It’s a poor mans ARC at best. ;)

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Marcos Douglas B. Santos
On Sat, Aug 18, 2018 at 2:04 PM, Ryan Joseph <[hidden email]> wrote:
>
> How does TAutoFree in mORMot work? Never heard of this.

See http://blog.synopse.info/post/2014/11/14/Automatic-TSQLRecord-memory-handling

regards,
Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Ryan Joseph
In reply to this post by Maciej Izak


> On Aug 17, 2018, at 7:50 PM, Maciej Izak <[hidden email]> wrote:
>
>     class operator Initialize(var a: TAutoCreate<T>);
>     class operator Finalize(var a: TAutoCreate<T>);
>

One other thing I wanted to ask you. I know it’s minor but for me it’s pretty disappointing that the Initialize and Finalize operators aren’t simply constructor/destructor since those names are available in records already (constructor with no parameter that is).

I say this because the syntax is long and hard to remember and don’t follow the usual convention of constructors/destructors. Initialize and Finalize are the most common operators you implement so it makes sense they would follow the normal syntax rules (c++ does this for classes declared on the stack).

Was there any consideration to making the default constructor and destructor be used instead of class operators?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Maciej Izak
In reply to this post by Marcos Douglas B. Santos
2018-08-18 20:26 GMT+02:00 Marcos Douglas B. Santos <[hidden email]>:
On Sat, Aug 18, 2018 at 2:04 PM, Ryan Joseph <[hidden email]> wrote:
>
> How does TAutoFree in mORMot work? Never heard of this.

See http://blog.synopse.info/post/2014/11/14/Automatic-TSQLRecord-memory-handling

worth to note that in FPC you additionally need to use "with ... do" (or use local variable IAutoFree), otherwise interface will be release immediately and code will be broken. The example in blog needs to be corrected:

=== code begin ===
function NewMaleBaby(Client: TSQLRest; const Name,Address: RawUTF8): TID;
var Baby: TSQLBaby;   
begin
  with TSQLBaby.AutoFree(Baby) do begin
    Baby.Name := Name;
    Baby.Address := Address;
    Baby.BirthDate := Date;
    Baby.Sex := sMale;
    result := Client.Add(Baby);
  end;
end;
=== code end ===

or you need to wait for {$MODESWITCH SCOPEDINTERFACEDESTROY} in NewPascal to use examples AS-IS (with SCOPEDINTERFACEDESTROY on). To be honest I forgot to send the patch with SCOPEDINTERFACEDESTROY to FPC when I had occasion/access to FPC trunk ^^ - anyway I was the only one who want this in the FPC core team for Delphi compatibility (and for large legacy projects where is really hard to adjust code which depends on SCOPEDINTERFACEDESTROY)... FPC core team said that this behavior of Delphi is not documented and not COM compatible (or is undefined in COM - I don't remember). Also there is some mystery example for large methods when Delphi is able to break the rule and such interface is released earlier (but no one was able to provide this example) - I was also trying without success, so SCOPEDINTERFACEDESTROY seems really Delphi compatible.

--
Best regards,
Maciej Izak

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Ryan Joseph
In reply to this post by Ryan Joseph


> On Aug 18, 2018, at 11:04 AM, Ryan Joseph <[hidden email]> wrote:
>
>> The solution which probably should be accepted by fpc team is default property without indexer:
>>
>> === code begin ===
>> property obj: T read fobj; default;
>> === code end ===
>>
>> with this property should be possible:
>>
>> === code begin ===
>> Writeln(o.ToString);
>> === code end ===
>
> How does that property work exactly? Making this work properly is the most important part but I never considered it. It sounds like “with” statements kind of.

Ok, I understand what you mean now. This is actually a really great idea for all sorts of other uses  because it helps to unify namespaces between classes and prevent long.and.annoying() chaining of fields.

Btw why should this be a property? I know [] properties use the default keyword also but I don’t understand where this came  from and it’s strange to name the [] property since the name is irrelevant. I would think you’d want to just put a modifier on the default field:

 TAutoCreate<T: class> = record
    _: T; default;
    class operator Initialize(var a: TAutoCreate<T>);
    class operator Finalize(var a: TAutoCreate<T>);
    class operator Copy(constref a: TAutoCreate<T>; var b: TAutoCreate<T>);
    class operator AddRef(var a: TAutoCreate<T>);
  end;

just curious and I thought I’d ask.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Auto vars (again)

Jonas Maebe-3
In reply to this post by Maciej Izak
On 18/08/18 23:18, Maciej Izak wrote:
> on SCOPEDINTERFACEDESTROY)... FPC core team said that this behavior of
> Delphi is not documented and not COM compatible (or is undefined in COM
> - I don't remember). Also there is some mystery example for large
> methods when Delphi is able to break the rule and such interface is
> released earlier (but no one was able to provide this example)

Here is one: https://bugs.freepascal.org/view.php?id=6035#c28656


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal