Kryvich 165 Posted November 15, 2018 @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
Cristian Peța 103 Posted November 15, 2018 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
Uwe Raabe 2057 Posted November 15, 2018 (edited) 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 November 15, 2018 by Uwe Raabe 2 Share this post Link to post
Kryvich 165 Posted November 15, 2018 @Uwe Raabe Ouch! The behavior depends of record size. Share this post Link to post
Cristian Peța 103 Posted November 15, 2018 15 minutes ago, Kryvich said: @Uwe Raabe Ouch! The behavior depends of record size. Compiler optimizations can bite sometimes. Share this post Link to post
Kryvich 165 Posted November 15, 2018 One more possibility with const parameter and a predictable behavior: procedure Test2(const [Ref] rec: TTestRec); begin rec.Change( 2 ); Writeln('Inside Test2: ', rec.Value ); // 2 end; http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Parameters_(Delphi)#Constant_Parameters Share this post Link to post
dummzeuch 1505 Posted November 15, 2018 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
Stefan Glienke 2002 Posted November 15, 2018 The second parameter of the Assign operator actually has to be either var or const [ref]. Share this post Link to post
Guest Posted November 15, 2018 (edited) 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 November 15, 2018 by Guest Share this post Link to post
Zacherl 3 Posted November 15, 2018 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
David Heffernan 2345 Posted November 15, 2018 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
Leif Uneus 43 Posted November 21, 2018 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&utm_medium=feed&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. 2 Share this post Link to post
Stefan Glienke 2002 Posted November 21, 2018 Better than releasing something that is broken and makes all your code fail, amiright? 3 Share this post Link to post
Leif Uneus 43 Posted November 21, 2018 @Stefan Glienke I could not agree more. Releasing software and updates based on a new compiler version is a risky business and tests are very time consuming. Share this post Link to post
Kryvich 165 Posted November 21, 2018 Well, well, well... As I understand they decided this at the last moment. Share this post Link to post
Rudy Velthuis 91 Posted January 3, 2019 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
Dalija Prasnikar 1396 Posted January 3, 2019 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