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 <pascaldra...@googlemail.com>: > 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. > 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. > 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
_______________________________________________ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel