Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2015-10

Pascal Smart Pointers Idea + ARC implementation

14 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Maciej Izak

I am working on smart pointers for FPC (something more than pure ARC for classes).

This solution is full compatible with existing code base.

I would like to know what do you think about my conception and work.

First step is almost done -> new record operators:

=== begin code ===
{$MODESWITCH MANAGEMENTOPERATORS}
type // Record with automatic constructor and destructor
  TValue = record
    class operator Initialize(var aRec: TValue);
    class operator Finalize(var aRec: TValue);
    class operator Copy(constref aSource: TValue; var aDest: TValue);
  end;
=== end code ===

MANAGEMENTOPERATORS are quite simple to implement. MANAGEMENTOPERATORS were created initially for fast implementation of TValue in RTTI.pas, but they inspired me for smart pointers.

The second step is some concept of low level structures for smart pointers (in this conception we have two kinds of smart pointers - smart pointers for objects and smart pointers for pointers):

=== code begin ===
  PRawSmartPtr = ^TRawSmartPtr;
  TRawSmartPtr = record
  private
    FInstance: Pointer;
    FRefCount: PLongint;
    function GetInstance: Pointer;
    function GetRefCount: Longint;
  public
    property Instance: Pointer read GetInstance;
    property RefCount: Longint read GetRefCount;
  end;
  PRawSmartObj = ^TRawSmartObj;
  TRawSmartObj = type TRawSmartPtr;
function TRawSmartPtr.GetInstance: Pointer;
begin
  if (FRefCount = nil) or (FRefCount^ <= 0) then
    Exit(nil);
  Result := FInstance;
end;
function TRawSmartPtr.GetRefCount: Longint
begin
  if FRefCount = nil then
    Exit(0);
  Result := FRefCount^;
end;

=== code end ===

For smart pointers we need to implement in some way ".", standard inplicit opertor, "^", "@" and "@@" operator. I thought about it for a long time. IMO minimal invasive method is "default" keyword, used as below:

=== code begin ===
  TSmartPtr<T> = record
    // similar as overloading [] operators for property x[v: string]:
integer read gx write sx; default;
    Instance: ^T; default; // default keyword for non property, can be used
only for field of pointer type.
    RefCount: PLongint;
    class operator Initialize(var aRec: TSmartPtr<T>);
    class operator Finalize(var aRec: TSmartPtr<T>);
    class operator Copy(constref aSource: TSmartPtr<T>; var aDest:
TSmartPtr<T>);
    // implicit or explicit operator should be used before "default" field
    operator Implicit(constref aValue: T); // special version of
Implicit/Explicit is also needed (available only when is used default for
field)
    operator Explicit: TRawSmartPtr;
  end;
class operator TSmartPtr<T>.Initialize(var aRec: TSmartPtr<T>);
begin
  aRec.RefCount := nil;
end;
class operator TSmartPtr<T>.Finalize(var aRec: TSmartPtr<T>);
begin
  if aRec.RefCount  nil then
    if InterLockedDecrement(aRec.RefCount^)=0 then
    begin
      Dispose(aRec.RefCount);
      Dispose(aRec.Instance);
    end;
end;
class operator TSmartPtr<T>.Copy(constref aSource: TSmartPtr<T>; var aDest:
TSmartPtr<T>);
begin
  if aDest.RefCount  nil then
    Finalize(aDest);
  if aSource.RefCount  nil then
    InterLockedIncrement(aSource.RefCount^);
  aDest.RefCount := aSource.RefCount;
  aDest.Instance := aSource.Instance;
end;
operator TSmartPtr<T>.Implicit(constref aValue: T);
begin
  if aDest.RefCount  nil then
    Finalize(aDest);
  New(RefCount);
  RefCount^ := 0;
  InterLockedIncrement(RefCount^);
  Instance := @aValue;
end;
operator TSmartPtr<T>.Explicit: TRawSmartPtr;
begin
  Result.RefCount := RefCount;
  Result.Instance := Instance;
end;

=== code end ===

TSmartObj<T: TObject> is very similar to TSmartPtr<T>, the difference exist inside Finalize - where is called Instance^.Free method instead of Dispose(aRec.Instance).

few examples:

=== code begin === simple use case
var
  myObj: TSmartObj<TObject>;
begin // <- call Initialize operator
  myObj := TObject.Create; // <- call Implicit operator
end; // <- call Finalize operator. Dec(RefCount) and if RefCount = 0 then
call Instance^.Free
=== code end ===
=== code begin === smart pointer and exceptions
var
  myObj: TSmartObj<TObject>;
begin // <- call Initialize operator
  myObj := TObject.Create; // <- call Implicit operator
  try // we don't need try-finally-end anymore because operator Finalize is
always called
    WriteLn(myObj.ClassName); // will print TObject. Equivalent of
WriteLn(p.Instance^.ClassName);
    raise Exception.Create('Error Message');
  except
  end;

end; // <- call Finalize operator. Dec(RefCount) and if RefCount = 0 then call Instance^.Free
=== code end ===

=== code begin === access to TSmartObj/TSmartPtr
var
  p: TSmartPtr<PInteger>;
begin // <- call Initialize operator
  WriteLn(TRawSmartPtr(p).RefCount); // <-- call Explicit . Will print 0
  p := New(PInteger); // <-- call Implicit
  p^ := 10; // use "default" magic, equivalent of p.Instance^^ := 10
  WriteLn(Assigned(p)); // will print true, equivalent of
WriteLn(Assigned(p.Instance^));
  WriteLn(TRawSmartPtr(p).RefCount); // <-- call Explicit . Will print 1
end; // <- call Finalize operator. Dec(RefCount) and if RefCount = 0 then
call Dispose(Instance)
=== code end ===

and so one :D. This solution is perfect for any Generics.Collections, fpc-stl, fgl library. We can use in simple way weak references and strong references with auto free memory of objects/pointers.

next step is syntax sugar (oxygene compatibility):

var
  SomeObj: unretained TSomeObject; // equivalent of SomeObj: TSomeObject;
  SomeObj: weak TSomeObject; // equivalent of SomeObj:
TWeakObj<TSomeObject>; <- TWeakObj is modified version of TSmartObj (to
rethink)
  SomeObj: strong TSomeObject; // equivalent of SomeObj:
TSmartObj<TSomeObject>;

next step is {$MODE DELPHINEXTGEN} - in this mode any object will be declared as TSmartObj.

Best regards, Maciej Izak

Reply by email

Sven Barth
to Maciej Izak
"Maciej Izak" wrote 13 lines
First step is almost done -> new record operators: === begin code === {$MODESWITCH MANAGEMENTOPERATORS} type // Record with automatic constructor and destructor TValue = record class operator Initialize(var aRec: TValue); class operator Finalize(var aRec: TValue); class operator Copy(constref aSource: TValue; var aDest: TValue); end; === end code === MANAGEMENTOPERATORS are quite simple to implement. MANAGEMENTOPERATORS

were created initially for fast implementation of TValue in RTTI.pas, but they inspired me for smart pointers.

This part I definitely like, because I wanted to implement them as well. ;) You don't need to hide these behind a modeswitch however as long as they are optional for a record as they don't conflict with existing operators.

The second step is some concept of low level structures for smart

pointers (in this conception we have two kinds of smart pointers - smart pointers for objects and smart pointers for pointers):

Would you please explain why you'd need this raw structure besides for printing the RecCounts? (honest question, maybe I'm still too tired ;) )

For smart pointers we need to implement in some way ".", standard

inplicit opertor, "^", "@" and "@@" operator. I thought about it for a long time. IMO minimal invasive method is "default" keyword, used as below:

This is where we reach the dangerous territory ;)
The problem with "default" is how to differentiate from a normal field usage of the record? Keep in mind that a feature might be used in different contexts than one first imagined. So how to ensure that one can access field X of the record and not field X of the default? Also for classes: is Instance a ^T as well or merely a T?

class operator TSmartPtr<T>.Copy(constref aSource: TSmartPtr<T>; var
aDest: TSmartPtr<T>);
Show quoted text 8 lines
begin if aDest.RefCount nil then Finalize(aDest); if aSource.RefCount nil then InterLockedIncrement(aSource.RefCount^); aDest.RefCount := aSource.RefCount; aDest.Instance := aSource.Instance; end;

I suppose Finalize here calls TSmartPtr.Finalize and not System.Finalize? We don't usually have directly callable operators, so keep that in mind.

next step is syntax sugar (oxygene compatibility):

Only behind a modeswitch or maybe even a mode Oxygene please. And take care with prefixes, they are a PITA to handle as I've already told you ;)

Regards,
Sven

Reply by email

Michael Van Canneyt
to Maciej Izak: I am working on smart pointers for FPC (something more than pure ARC for classes). This…
Maciej Izak wrote 22 lines
I am working on smart pointers for FPC (something more than pure ARC for classes). This solution is full compatible with existing code base. I would like to know what do you think about my conception and work. First step is almost done -> new record operators: === begin code === {$MODESWITCH MANAGEMENTOPERATORS} type // Record with automatic constructor and destructor TValue = record class operator Initialize(var aRec: TValue); class operator Finalize(var aRec: TValue); class operator Copy(constref aSource: TValue; var aDest: TValue); end; === end code === MANAGEMENTOPERATORS are quite simple to implement. MANAGEMENTOPERATORS were created initially for fast implementation of TValue in RTTI.pas, but they inspired me for smart pointers.

This part I understand, it is simple enough. A managed record type ?

The second step is some concept of low level structures for smart pointers (in this conception we have two kinds of smart pointers - smart pointers for objects and smart pointers for pointers):

As I understand the code, this is the low-level basis for a managed pointer, and by inference, a managed class. Is that correct ?

That would mean that the managed pointer/class would be implemented internally using a 'managed record', correct ? I like this approach, quite ingenious :)

If so, please put the activation of the managed pointer feature under a modeswitch, as Sven suggested. {$MODE DELPHINEXTGEN} or {$MODE OXYGENE} can simply allow this modeswitch.

And: keep up the good work :)

Michael.

Reply by email

Sven Barth
to Michael Van Canneyt
"Michael Van Canneyt" wrote 25 lines
On Sat, 10 Oct 2015, Maciej Izak wrote: > I am working on smart pointers for FPC (something more than pure ARC for > classes). > > This solution is full compatible with existing code base. > > I would like to know what do you think about my conception and work. > > First step is almost done -> new record operators: > > === begin code === > {$MODESWITCH MANAGEMENTOPERATORS} > type // Record with automatic constructor and destructor > TValue = record > class operator Initialize(var aRec: TValue); > class operator Finalize(var aRec: TValue); > class operator Copy(constref aSource: TValue; var aDest: TValue); > end; > === end code === > > MANAGEMENTOPERATORS are quite simple to implement. MANAGEMENTOPERATORS

were

Show quoted text 5 lines
> created initially for fast implementation of TValue in RTTI.pas, but they > inspired me for smart pointers. This part I understand, it is simple enough. A managed record type ?

I don't think a different type will be needed. Just allow those three operator overloads for normal records and have them be present in the Init RTTI of the record so that fpc_initialize(), fpc_finalize() and fpc_copy() can pick them up. This will be needed anyway should such records be placed in arrays or other records or classes. (Of course from great power comes great responsibility ;) )

That would mean that the managed pointer/class would be implemented

internally using a 'managed record', correct ? I like this approach, quite ingenious :)

It's similar to the approach we use at work in C++. Simplyfies things quite a bit when one doesn't have to deal with delete ;)

Regards,
Sven

Reply by email

Michael Van Canneyt
to Sven Barth
Sven Barth wrote 34 lines
Am 10.10.2015 11:12 schrieb "Michael Van Canneyt" : > > > > On Sat, 10 Oct 2015, Maciej Izak wrote: > >> I am working on smart pointers for FPC (something more than pure ARC for >> classes). >> >> This solution is full compatible with existing code base. >> >> I would like to know what do you think about my conception and work. >> >> First step is almost done -> new record operators: >> >> === begin code === >> {$MODESWITCH MANAGEMENTOPERATORS} >> type // Record with automatic constructor and destructor >> TValue = record >> class operator Initialize(var aRec: TValue); >> class operator Finalize(var aRec: TValue); >> class operator Copy(constref aSource: TValue; var aDest: TValue); >> end; >> === end code === >> >> MANAGEMENTOPERATORS are quite simple to implement. MANAGEMENTOPERATORS were >> created initially for fast implementation of TValue in RTTI.pas, but they >> inspired me for smart pointers. > > > This part I understand, it is simple enough. A managed record type ? I don't think a different type will be needed.

I'm not suggesting it either.
Just looking for a term that concisely describes what it is :)

Show quoted text 6 lines
Just allow those three operator overloads for normal records and have them be present in the Init RTTI of the record so that fpc_initialize(), fpc_finalize() and fpc_copy() can pick them up. This will be needed anyway should such records be placed in arrays or other records or classes. (Of course from great power comes great responsibility ;) )

I don't know how this works internally.
Is fpc_initialize called for every record type ? I sincerely hope not. Does the compiler check the actual RTTI to decide whether fpc_initialize and friends are called ?

Michael.

Reply by email

Sven Barth
to Michael Van Canneyt
"Michael Van Canneyt" wrote
> Just allow those three > operator overloads for normal records and have them be present in the

Init

> RTTI of the record so that fpc_initialize(), fpc_finalize() and

fpc_copy()

> can pick them up. This will be needed anyway should such records be

placed

Show quoted text 6 lines
> in arrays or other records or classes. (Of course from great power comes > great responsibility ;) ) I don't know how this works internally. Is fpc_initialize called for every record type ? I sincerely hope not.

For every variant, managed type and record with init RTTI (which is the case if the record contains a variant, a managed type or a record with init RTTI). Same for fpc-copy and fpc_finalize. Records that contain managed operators would simply fall under the category "record with init RTTI as well", those that don't wouldn't have an init RTTI (at least if they don't fullfill the criteria for it anyway ;) ).

Does the compiler check the actual RTTI to decide whether fpc_initialize

and friends are called ?

As said above only on types that directly or indirectly need it.

Regards,
Sven

Reply by email

Michael Van Canneyt
to Sven Barth
Sven Barth wrote 26 lines
Am 10.10.2015 12:15 schrieb "Michael Van Canneyt" : >> Just allow those three >> operator overloads for normal records and have them be present in the Init >> RTTI of the record so that fpc_initialize(), fpc_finalize() and fpc_copy() >> can pick them up. This will be needed anyway should such records be placed >> in arrays or other records or classes. (Of course from great power comes >> great responsibility ;) ) > > > I don't know how this works internally. > Is fpc_initialize called for every record type ? I sincerely hope not. For every variant, managed type and record with init RTTI (which is the case if the record contains a variant, a managed type or a record with init RTTI). Same for fpc-copy and fpc_finalize. Records that contain managed operators would simply fall under the category "record with init RTTI as well", those that don't wouldn't have an init RTTI (at least if they don't fullfill the criteria for it anyway ;) ). > Does the compiler check the actual RTTI to decide whether fpc_initialize and friends are called ? As said above only on types that directly or indirectly need it.

All clear, and I hope to see an ARC implementation based on this "really soon" :-)

Michael.

Reply by email

Maciej Izak
to Sven Barth: were created initially for fast implementation of TValue in RTTI.pas, but they inspired…

First of all there was small bug in my previous code: wrong usage of Finalize, lack of SmartFinalize and bad parameter for Implicit (keep in mind that this structure will be complemented for TWeakPtr). Correct version:

======begin code======
type
  TSmartPtr<T> = record
    // similar as overloading [] operators for property x[v: string]:
integer read gx write sx; default;
    Instance: T; default; // default keyword for non property, can be used
only for field of pointer type.
    RefCount: PLongint;
    procedure SmartFinalize();
    class operator Initialize(var aRec: TSmartPtr<T>);
    class operator Finalize(var aRec: TSmartPtr<T>);
    class operator Copy(constref aSource: TSmartPtr<T>; var aDest:
TSmartPtr<T>);
    // implicit or explicit operator should be used before "default" field
    operator Implicit(const aValue: T); // special version of
Implicit/Explicit is also needed (available only when is used default for
field)
    operator Explicit: TRawSmartPtr;
  end;
procedure TSmartPtr<T>.SmartFinalize();
begin
  if RefCount  nil then
    if InterLockedDecrement(RefCount^)=0 then
    begin
      Dispose(RefCount);
      Dispose(Instance);
    end;
end;
class operator TSmartPtr<T>.Initialize(var aRec: TSmartPtr<T>);
begin
  aRec.RefCount := nil;
end;
class operator TSmartPtr<T>.Finalize(var aRec: TSmartPtr<T>);
begin
  aRec.SmartFinalize();
end;
class operator TSmartPtr<T>.Copy(constref aSource: TSmartPtr<T>; var aDest:
TSmartPtr<T>);
begin
  if aDest.RefCount  nil then
    Finalize(aDest);
  if aSource.RefCount  nil then
    InterLockedIncrement(aSource.RefCount^);
  aDest.RefCount := aSource.RefCount;
  aDest.Instance := aSource.Instance;
end;
operator TSmartPtr<T>.Implicit(const aValue: T);
begin
  if aDest.RefCount  nil then
    aDest.SmartFinalize();
  New(RefCount);
  RefCount^ := 0;
  InterLockedIncrement(RefCount^);
  Instance := aValue;
end;
operator TSmartPtr<T>.Explicit: TRawSmartPtr;
begin
  Result.RefCount := RefCount;
  Result.Instance := Instance;
end;
======end code======

2015-10-10 9:59 GMT+02:00 Sven Barth :

Would you please explain why you'd need this raw structure besides for printing the RecCounts? (honest question, maybe I'm still too tired ;) ) There is no way to access RecCount from TSmartPtr. When is used "default"

inside TSmartPtr (or inside any other record), TSmartPtr works only as proxy object to "Instance" field. (!) but to perform functionality is necessary to treat Implicit and Explicit operators as operators with higher priority than "default" field, an example:

===begin code===
var
  x: TSmartPtr<PInteger>;
  i: Integer;
begin
  x := New(PInteger); // Implicit operator instead of default "Instance"
field (the operator Implicit has precedence)
  WriteLn(x^); // default field "Instance" is used, will print some random
number
  x := 'foo'; // Error: Incompatible types: got "Constant String" expected
"PInteger" (no Implicit operator found so is used  default "Instance" field
  i := x.RecCount; // Error: Illegal qualifier (no implicit operator found
so is used default "Instance" field)
  TSmartPtr<PInteger>(x).Instance^ := 10; // Error: Illegal type
conversion: "PInteger" to TSmartPtr<PInteger> (no Explicit operator found
for TSmartPtr so is used proxy default "Instance" field.
  // Btw Explicit nor Implicit operator has no sense here because "default"
field will be always block access for other record structures)
  // in that situation we need raw structure with similar memory/structures
layout
  TRawSmartPtr(x).Instance^ := 5; // Explicit operator instead of default
"Instance" field (the operator Explicit has precedence)
end;
===end code===

!!! WARNING -> Explicit and Implicit in that case is different than standard Explicit / Implicit. To be precise we have two new operators:

===begin code===
operator Explicit: A;
operator Implicit(const Value: B);
// there is no class keyword before declaration, look at example
implementation of TSmartPtr
===end code===

these two operators have a higher priority than "default" field, and "default" field have higher priority than any other field/property/operator/method/const/type inside record structure. When "default" is used for field then we don't have direct access to record. New variant of Explicit operator and raw structure is IMO elegant way to get access for original record fields.

Show quoted text 6 lines
This is where we reach the dangerous territory ;) The problem with "default" is how to differentiate from a normal field usage of the record? Keep in mind that a feature might be used in different contexts than one first imagined. So how to ensure that one can access field X of the record and not field X of the default?

I know it is risky :P. In attached proposition, problem of overloading operators like "@", ".", "^" is omitted. You need to start thinking about "default" as proxy. Mentioned problem don't exist because programmer can access only "Instance" field (but not directly, for this is used "compiler magic"). The only place where exist access to record structures is implementation of this record. From implementation is possible access to any field or property. Inside implementation of record with "default" field, "default" field has no effect:

===begin code===
type
  TFoo = record
    { ... }
    a: PInteger; default;
    property x[v: string]: integer read gx write sx; default;
    procedure Foo;
  end;
procedure TFoo.Foo()
begin
  Self['x'] := 10; // is ok
end;
var
  f: TFoo;
begin
  f['x'] := 10; // equivalent of f.a['x'] := 10;
  // ^^^ Error: Incompatible types: got "Constant String" expected
"LongInt"
===end code===
Also for classes: is Instance a ^T as well or merely a T?

You are right, my bad. I was tired :P. We need only "T" even for TSmartPtr.

Show quoted text 10 lines
I suppose Finalize here calls TSmartPtr.Finalize and not System.Finalize? We don't usually have directly callable operators, so keep that in mind. Right. Fixed on top this message ;). Only behind a modeswitch or maybe even a mode Oxygene please. And take care with prefixes, they are a PITA to handle as I've already told you ;) do not scare me!

Wit presented syntax is possible to create many variants of Smart Pointers with different functionality. Influence of "default" field can may be weakened by introducing new operators like new Implicit / Explicit (for example operator TypeInfo).

Few more examples:

var
  a, b: TSmartObj<TObject>;
begin // TSmartObj<TObject>.Initialize(a); TSmartObj<TObject>.Initialize(b);
  a := TObject.Create; // a.Implicit(TObject.Create);
  b := a; // TSmartObj<TObject>.Copy(a, b);
  FreeAndNil(a); // FreeAndNil(a.Instance);
  WriteLn(a = b);// a.Instance = b.Instance // print false
  // we need to clear b
  b := nil; // b.Instance := nil;
  WriteLn(TypeInfo(a) = TypeInfo(TSmartObj<TObject>)) //
TypeInfo(a.Instance) = TypeInfo(TSmartObj<TObject>) // print false
Best regards, Maciej Izak

Reply by email

Sven Barth
to Maciej Izak
"Maciej Izak" wrote
class operator TSmartPtr<T>.Copy(constref aSource: TSmartPtr<T>; var
aDest: TSmartPtr<T>);
Show quoted text 8 lines
begin if aDest.RefCount nil then Finalize(aDest); if aSource.RefCount nil then InterLockedIncrement(aSource.RefCount^); aDest.RefCount := aSource.RefCount; aDest.Instance := aSource.Instance; end;

Shouldn't it be SmartFinalize here as well instead of Finalize?

2015-10-10 9:59 GMT+02:00 Sven Barth : > > Would you please explain why you'd need this raw structure besides for

printing the RecCounts? (honest question, maybe I'm still too tired ;) )

There is no way to access RecCount from TSmartPtr. When is used "default"

inside TSmartPtr (or inside any other record), TSmartPtr works only as proxy object to "Instance" field. (!) but to perform functionality is necessary to treat Implicit and Explicit operators as operators with higher priority than "default" field, an example:

Ah, yes, now I see.

!!! WARNING -> Explicit and Implicit in that case is different than

standard Explicit / Implicit. To be precise we have two new operators:

Show quoted text 5 lines
===begin code=== operator Explicit: A; operator Implicit(const Value: B); // there is no class keyword before declaration, look at example

implementation of TSmartPtr

===end code=== these two operators have a higher priority than "default" field, and

"default" field have higher priority than any other field/property/operator/method/const/type inside record structure. When "default" is used for field then we don't have direct access to record. New variant of Explicit operator and raw structure is IMO elegant way to get access for original record fields.

Playing around with priorities can be a PITA as well, so be careful ;)

> > This is where we reach the dangerous territory ;) > The problem with "default" is how to differentiate from a normal field

usage of the record? Keep in mind that a feature might be used in different contexts than one first imagined. So how to ensure that one can access field X of the record and not field X of the default?

I know it is risky :P. In attached proposition, problem of overloading

operators like "@", ".", "^" is omitted. You need to start thinking about "default" as proxy. Mentioned problem don't exist because programmer can access only "Instance" field (but not directly, for this is used "compiler magic"). The only place where exist access to record structures is implementation of this record. From implementation is possible access to any field or property. Inside implementation of record with "default" field, "default" field has no effect:

While I see and understand your reasoning behind it I don't really like it... There needs to be some better way for this... hmm... :/

> Only behind a modeswitch or maybe even a mode Oxygene please. And take

care with prefixes, they are a PITA to handle as I've already told you ;)

do not scare me!

Who? Me?! Never! :P

Regards,
Sven

Reply by email

Maciej Izak
to Sven Barth

2015-10-10 18:40 GMT+02:00 Sven Barth :

Show quoted text 14 lines
Am 10.10.2015 18:19 schrieb "Maciej Izak" : > class operator TSmartPtr<T>.Copy(constref aSource: TSmartPtr<T>; var aDest: TSmartPtr<T>); > begin > if aDest.RefCount nil then > Finalize(aDest); > if aSource.RefCount nil then > InterLockedIncrement(aSource.RefCount^); > aDest.RefCount := aSource.RefCount; > aDest.Instance := aSource.Instance; > end; Shouldn't it be SmartFinalize here as well instead of Finalize?

Yes, it should be SmartFinalize.

While I see and understand your reasoning behind it I don't really like it... There needs to be some better way for this... hmm... :/

I am only sure about MANAGEMENTOPERATORS.

instead of "default" field we can introduce:

operator Dot: T;
operator Dereference: T;
operator Addr: T;
operator DoubleAddr: T;
operator DefaultImplicit: T;
operator TypeInfo: PTypeInfo;

but this solution is much slower. btw. we can introduce also this operators for better control of "default" field. (all of above operator have higher priority than "default" field and "default" field has bigger priority than class operator).

Best regards, Maciej Izak

Reply by email

Sven Barth
to Maciej Izak: 2015-10-10 18:40 GMT+02:00 Sven Barth : Yes, it should be SmartFinalize. I am only sure…
Maciej Izak wrote 20 lines
2015-10-10 18:40 GMT+02:00 Sven Barth < >: While I see and understand your reasoning behind it I don't really like it... There needs to be some better way for this... hmm... :/ I am only sure about MANAGEMENTOPERATORS. instead of "default" field we can introduce: operator Dot: T; operator Dereference: T; operator Addr: T; operator DoubleAddr: T; operator DefaultImplicit: T; operator TypeInfo: PTypeInfo; but this solution is much slower. btw. we can introduce also this operators for better control of "default" field. (all of above operator have higher priority than "default" field and "default" field has bigger priority than class operator).

They don't necessarily need to be slower. If you declare them as "inline" then the compiler will indeed inline them if it sees the potential for it.
(Sidenote: non-Delphi modes should use the operator symbols, namely ".", "^", "@" and "@@" (though I have to admit that I don't know where you got that @@ from; was that introduced in Delphi recently?)).

It's not an easy topic and I'd really welcome the input of other core developers...

Regards,
Sven

Reply by email

Maciej Izak
to Sven Barth

2015-10-10 21:12 GMT+02:00 Sven Barth :

Show quoted text 5 lines
It's not an easy topic and I'd really welcome the input of other core developers... Regards, Sven

That would be great. Especially interesting for me is opinion about "default" for field (IMO better than introducing many new operators). Maybe it is absolutely wrong way?

Best regards, Maciej Izak

Reply by email

Maciej Izak
to Sven Barth: They don't necessarily need to be slower. If you declare them as "inline" then the…

2015-10-10 21:12 GMT+02:00 Sven Barth :

Show quoted text 7 lines
"@@" (though I have to admit that I don't know where you got that @@ from; was that introduced in Delphi recently?)). It's not an easy topic and I'd really welcome the input of other core developers...

Somehow I forgot to answer...

@@ is old syntax (maybe even from TP?). @@ is used to get pointer to variable which holds pointer to procedure/function.

=== begin code ===
procedure foo; begin end;
var
  p: procedure;
  pp: pointer;
begin
  Assert(@p = nil);
  Assert(addr(p) = nil);
  pp := @@p;
  Assert(pp  nil);
  p := @foo;
  Assert(@p = @foo);
  Assert(addr(p) = addr(foo));
  Assert(pp = @@p);
end.
=== end code ===

another way to get pointer to that kind of variable instead of @@ is:

=== begin code ===
function getptr(var x): pointer;
begin
  Result := @x;
end;
var
  p: procedure;
  pp: pointer;
begin
  pp := getptr(p);
=== end code ===
Best regards, Maciej Izak

Reply by email