Jump to content
Marco Cantu

Custom Managed Records Coming in Delphi 10.3

Recommended Posts

@Schokohase But if you check the value inside Test2:

  rec.Change( 2 );
  Writeln('Inside Test2: ', rec.Value ); // 2

Now I understand why they use var instead of const in the constructor: the const record parameter passed by value, not by reference.

Share this post


Link to post

For protection against modifying const parameters the language shouldn't have pointers.

When ARC was introduced with iOS compiler there was a warning that in the future pointers can be banned by NEXTGEN compilers. But I don't think this will ever happen.

Share this post


Link to post
2 hours ago, Schokohase said:

I can only change the var records.

Extend the record a bit and try again (so it doesn't fit into a register):

  TTestRec = record
    dummy: array[1..10] of Integer;
    Value: Integer;
    procedure Change( AValue: Integer );
  end;

 

Edited by Uwe Raabe
  • Like 2

Share this post


Link to post
2 hours ago, Schokohase said:

Well, if you pass a pointer to memory from your own process you should not be surprised that you can modify the memory you are pointing at. I would be surprised if I could not.

But that's what const is all about: A parameter (possibly passed by reference) where the compiler prevents changes.

 

Turns out that it is easy to change it by mistake. (Of course it's also not simple for the compiler to catch this case.)

Share this post


Link to post

Ok, here an extended sample without pointer arguments ... but with pointer logic ... and the extended record from @Uwe Raabe

uses
  System.SysUtils;

type
  PTestRec = ^TTestRec;

  TTestRec = record
    dummy: array [1 .. 10] of Integer;
    Value: Integer;
    procedure Change( AValue: Integer );
  end;

  { TTestRec }

procedure TTestRec.Change( AValue: Integer );
begin
  Self.Value := AValue;
end;

procedure Test1( const rec: TTestRec );
begin
  // will not compile
  // rec.Value := 1;
end;

procedure Test2( const rec: TTestRec );
begin
  rec.Change( 2 );
end;

procedure Test3( rec: TTestRec );
begin
  rec.Value := 3;
end;

procedure Test4( rec: TTestRec );
begin
  rec.Change( 4 );
end;

procedure Test5( var rec: TTestRec );
begin
  rec.Value := 5;
end;

procedure Test6( var rec: TTestRec );
begin
  rec.Change( 6 );
end;

procedure Test7( const rec: TTestRec );
var
  lrec: PTestRec;
begin
  lrec       := @rec;
  lrec.Value := 7;
end;

procedure Test8( rec: TTestRec );
var
  lrec: PTestRec;
begin
  lrec       := @rec;
  lrec.Value := 8;
end;

procedure Main;
var
  rec: TTestRec;
begin
  rec.Value := 0;
  Test1( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test2( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test3( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test4( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test5( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test6( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test7( rec );
  Writeln( rec.Value );
  rec.Value := 0;
  Test8( rec );
  Writeln( rec.Value );
end;

begin
  try
    Main( );
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  Readln;

end.

and the result ist

0 <-- expected
2
0 <-- expected
0 <-- expected
5 <-- expected
6 <-- expected
7
0 <-- expected

Test 3, 4 and 8 are working on a copy, so that should never break.

Edited by Schokohase

Share this post


Link to post

The var can actually make sense, if you think of the C++ move semantics. This way you could either implement a copy or a move assignment operator ... Would be nice if there was a way to implement both (same for constructors). 

Share this post


Link to post
7 hours ago, Schokohase said:

Well, if you pass a pointer to memory from your own process you should not be surprised that you can modify the memory you are pointing at. I would be surprised if I could not.

In C++ you can prevent it from happening. 

 

Share this post


Link to post

Unfortunately the introduction of custom managed records has been deferred to version 10.4

 

See http://blog.marcocantu.com/blog/2018-november-deferring-managed records.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+marcocantublog+(marcocantu.blog)

 

Pity, since I can see many important use cases for this language enhancement. Guess I will have to bide my time another year.

  • Like 2

Share this post


Link to post

Well, well, well... As I understand they decided this at the last moment.

Share this post


Link to post
On 11/7/2018 at 11:41 PM, Dalija Prasnikar said:

Automatic invocation is the whole point of managed records. If they have constructors and destructors, you want them to run.

Yes, you can do that manually, but then they would not be called managed records. 

 

If you don't want managed records, and you want to perform some initialization and finalization code manually, you can use plain record procedures. You don't need constructors and destructors in that case.

Automatic invocation of the (parameterless) default constructor (and a copy constructor -- with one parameter of the same type as the record) is the whole point. All other constructors should still be called explicitly, just like in C++, so no need to turn things into class functions. There should only be one destructor, and that should also automatically be called. Actually, that is the most important thing about it. Even if the constructor were explicit, you would still want an automatically called destructor.

Share this post


Link to post
8 hours ago, Rudy Velthuis said:

Automatic invocation of the (parameterless) default constructor (and a copy constructor -- with one parameter of the same type as the record) is the whole point. All other constructors should still be called explicitly, just like in C++, so no need to turn things into class functions. There should only be one destructor, and that should also automatically be called. Actually, that is the most important thing about it. Even if the constructor were explicit, you would still want an automatically called destructor.

Yes, of course.

 

The only time you don't want destructor to run is when you didn't write one and your record does not contain any managed fields that need finalization. 

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

×