@Warfley: Are you sure your example is correct?
Yes, it's about the copy operator that is overloaded. This is used when assigning variables of the same record type:
var
t1, t2: TMStream;
begin
t2 := t1; // internally this will be compiled to TMStream.Copy(t1, t2);
end;
A lot of other data also works via pointer in Pascal:
- Objects (instances of classes, not old style object).
- Dyn Array
On the other hand records are passed by value. But you can specify "var" or "constref" depending on what you need.
But this is the thing, with the management operators you can define your own assignment semantics to records. This is useful for having managed datatypes inside your records. There are multiple reasons why these are very useful, most importantly, it let's you write local datatypes that do not have the overhead of classes, and support things like operator overloading for example.
The problem here is that as soon as you use operator overloading, the amount of copies is really annoying. For example I wrote a gmp wrapper recently, here are some parts of the code:
class operator TAPInteger.Finalize(var a: TAPInteger);
begin
mpz_clear(a.FData);
end;
class operator TAPInteger.Copy(constref aSrc: TAPInteger;
var aDst: TAPInteger);
begin
mpz_set(aDst.FData, PAPInteger(@aSrc)^.FData); // deepcopies the whole data
end;
class operator TAPInteger.+(constref lhs: TAPInteger; constref
rhs: TAPInteger): TAPInteger;
begin
mpz_add(Result.FData, PAPInteger(@lhs)^.FData, PAPInteger(@rhs)^.FData);
end;
The following expression c := a + b; would create a temporary object that is used as result of the + operation, which is then copied into c using the copy operator.
I wanted to implement some crypto algorithms just out of interest, i.e. not write production ready code, therefore the copies are not a problem, but if you wanted to deploy this code in a server that has to be quick when establishing handshakes and stuff (as each copy would need to copy around 500 bytes), this would be a problem.
The C++ OOP implementation of the gmp uses for this the move semantic, i.e. a temporary object gets created, but the assignment to c only copies the pointer not the whole data.
To do this in pascal you would need to completely go without operator overloading. And personally I think this makes the code much worse. Just look at the RSA key generation:
p := TAPInteger.RandomPrime;
q := TAPInteger.RandomPrime;
m := p * q;
phi := (p-1) * (q-1);
pub := 65537;
priv := pub.inverse(phi);
this is much better than writing the following:
mpz_init(p);
mpz_init(q);
mpz_init(m);
mpz_init(phi);
mpz_init_set_ui(pub, 65537);
mpz_init(priv);
generateRandomPrime(p);
generateRandomPrime(q);
mpz_mul(m, p, q);
mpz_init(phi_p);
mpz_init(phi_q);
mpz_sub_ui(phi_p, p, 1);
mpz_sub_ui(phi_q, q, 1);
mpz_mul(phi, phi_p, phi_q);
mpz_clear(phi_p);
mpz_crear(phi_q);
mpz_inverse(priv, pub, phi);
In C++ you can write code like the former with literally no drawbacks, in pascal if you need performance, you need to write the latter one if you don't want to loose a lot of performance due to copying.
PS: I should note that due to
this bug (#37164) (double call to the finalize operator in functions that return managed records) management operators are currently completely unusable (as they can simply not used as return values, and therefore constructors and operators are unusable) and my project actually didn't go anywhere because of this, but this is about conceptual designs of the language not bugs in the compiler. And still, even though when this bug gets fixed I will continue my projects, the code I write will not be production ready due to this massive overhead and I probably, if I ever need to use the GMP, will resort to C++ instead