I've been battling with this problem for many months now, and would welcome any suggestions.
Why not use a property for setting values of record fields?
I didn’t even know that constref existed before."constref" has nothing to do with multi-threaded programs. It is a way to tell the compiler to pass the parameter by reference and that in spite of it being passed by reference, the parameter cannot be modified, i.e, is constant. "constref" = constant by reference (reference = pass the address of the structure.)
https://wiki.freepascal.org/Constref
Is this is for multi threaded programs?
This qualifier informs the compiler that within the entire program there is no code that will change the value of the parameter while the procedure/function is executing.
This means that not only the parameter, but also the variable (in above example: x) passed by the caller (e.g. a global var) must not be changed until the call with the constref parameter has returned.
This qualifier informs the compiler that within the entire program there is no code that will change the value of the parameter while the procedure/function is executing.
Switch to the French version, the truth is there.I rather keep it to the official documentation about constant parameters (https://www.freepascal.org/docs-html/ref/refsu67.html)
Also what does this example code show?AFAICS it tries to show you, that you can call a /reference/ parameter(constref) with a literal value too...
This qualifier informs the compiler that within the entire program there is no code that will change the value of the parameter while the procedure/function is executing.
That is so unbeliveable I had to test it. And it turns out to NOT be true. A variable CAN be modified while some other rountine is "using" the variable as a constref.
V:= U;
Quote
V:= U;
The record contains a dyn-array. Which is a pointer. The above statement copies that pointer.
V.I and U.I are now the same array (with a ref count of 2).
I thought it might be for multi threaded programs because ofQuoteThis qualifier informs the compiler that within the entire program there is no code that will change the value of the parameter while the procedure/function is executing.
This means that not only the parameter, but also the variable (in above example: x) passed by the caller (e.g. a global var) must not be changed until the call with the constref parameter has returned.
If the compiler can help us with that, that is very useful. And 440bx already explained it well.
You may perceive it as "help by the compiler" => but it is not.
...
Is you helping the compiler (helping to optimize). Because you are telling the compiler: "foo will not change. Go ahead optimize".
And that means not to change by any means.
After calling the TrashREC procedure, the first array element of U is also incorrectly overwritten, as follows:
U=99 99 99 99 99
V=99 99 99 99 99
TrashREC
U=100 99 99 99 99
V=100 99 99 99 99
While investigating this, I found this page in the wiki...
https://wiki.freepascal.org/management_operators (https://wiki.freepascal.org/management_operators)
This gives an explanation of what is happening, and a solution to the problem. The fixed/working program is:
Les notes de version de la version 2.6 suggère que vous ne devriez utiliser ceci que pour l'interface avec des routines externes écrites dans d'autres langages, où ce type de passage de paramètre est requis.I did sound a bit far fetched that passing a value as a constref could stop it being changed in any other part of the program. :D
It is the programmer who tells the compiler that the contents of the const parameter will not be changed when the routine is executed
And currently no version of fpc does any optimization (that I know of) that would lead to an error as result of this violation. However, a future version may have such optimizations.
So this is actually breaking the const rule.I've come to interpret the "const rule" a little bit differently than what the FPC documentation states.
Is there any way to prevent the fields of a record Or object passed as a constant from being modified?Depends...
Is there any way to prevent the fields of a record Or object passed as a constant from being modified?If the record is compiled in {$J-} state, you can not modify it. (It usually ends up in .rodata, read-only data.)
That's why I said that it is ultimately the programmer's responsibility to ensure there are no undesirable side effects.
For instance, the compiler does not know if some area of memory being referenced in some function/procedure is also being referenced by a different function/procedure in another thread. It is the programmer who must know and account for situations like that.
Interesting tricks but still it’s kind of misleading declaring a record Parameterconst if it in fact is not. :D
Long story short, you as a programmer only ever are responsible to follow the semantics of the language.For that part, I qualify that with "most of the time" or "if convenient".
If something falls outside of the defined behavior of the language, you should not make any assumptions about itI definitely don't make any assumptions. I make sure the compiler behaves as I expect it to and, not only that, as it _has_ to.
I definitely don't make any assumptions. I make sure the compiler behaves as I expect it to and, not only that, as it _has_ to.
That's the reason I only use "const" when dealing with ordinal types and I am fully aware that some types are ordinal in3264 bit but not in6432 bit, e.g, qword. Anything I haven't done in the past, I make it a point to look at the generated assembler to ensure it is the way I expect it to be (and the way the compiler should be coding it.)
I whole heartedly disagree with that approach.I have no doubt you have plenty of company there.
When using a high level language you should not make any assumptions (or expectations) about the underlying assembly.I will say it again: I don't make assumptions and I verify that the compiler did what I expected it to do.
I don't make assumptions and I verify that the compiler did what I expected it to do.Assumptions that you verify are still assumptions. Note that when I googled "expectation synonym" the very first result was "assumption" :)
Compilers don't do magic, they follow rules to generate code, that's true of optimizers too, they follow rules. Compilers, unlike programmers cannot break the rules because if they do, that's reported as a bug. If a compiler breaks a rule I need it to enforce, I'll change the code to have it generate the code I want. IOW, the compiler is tool that works for _me_, I don't work for the compiler.
Assumptions that you verify are still assumptions. Note that when I googled "expectation synonym" the very first result was "assumption" :)Personally, in my book, once something is verified it is no longer an assumption. It's kinda like that by definition.
But these rules can change with future versions of the Compiler, or when used on different targets.That's true. In those cases the assumption(s), if any, need to be verified again to account for the changes that took place.
The reason you have this problem is quite simply is that dynamic arrays are (unlinke dynamic strings) not copy on write. Meaning if you have multiple references to the same array, it will not be made unique on access.I find all this very disappointing.
For strings there is the function UniqueString (https://www.freepascal.org/docs-html/rtl/system/uniquestring.html), but afaik nothing comparable exists for arrays yet.
For your purposes the most simple way of doing the deep copy would be to simply use setlength, as it ensures a refcount of 1, meaning it can be used to create a unique copy:
class operator REC.copy(constref v1:REC; var V2:REC); begin v2.I:=v1.I; SetLength(v2.I, Length(v2.I)); end;
Personally I think a deepcopy intrinisc utilizing RTTI would be a very useful addition to the compiler. All thats needed to implement it is already part of the InitRTTI table used for managed types anyway
I find all this very disappointing.
I've programmed in low-level languages like assembly and C, so I know about all these complexities.
But my view is that a high-level language should hide the low-level details so that you don't have to worry about them, and the code does what you would expect. Programmers should not have know the deep internals of how data types are represented and copied to make their programs work.
If the programmer wants to gain the efficiency of copy-on-write and having the internals of records be pointers to external data objects, then I think that those features should be enabled by a switch, and with the switch having a warning that hidden dangers are present. Then expert programmers, who understand the implications, can turn the features on if they wish.
An array is just a dumb pointer, that is copied around when you copy the record.My point is this...
The implications these kinds of complexities, means that no-one can safely code in Pascal until they are an expert that knows everything, right down to all the obscure details of how the compiler works. I.M.O. this defeats the whole philosophy of Pascal as being a safe language.No compiler can compensate for an inadequate level of knowledge in a programmer.
I did not understand the internals of how the compiler dealt with a record containing a dynamic array. Then when I wrote the code v1:= v2; procl(v1,v2); expecting the behaviour to be the same as any other type, it did not work. The proc_call overwote v1 as well as v2, even though in the definition of proc, v1 was specified as const.That is not special knowledge, if you know how records work and you know how dynamic arrays work, it's exactly what you would expect. If you don't know either, you should read into that before using those types.