Quantcast
Channel: Delphi – The Wiert Corner – irregular stream of stuff
Viewing all articles
Browse latest Browse all 1440

Want to use SmartPointers in Delphi? Use the Spring4D one: it is unit tested and optimised

$
0
0

A while ago, there was a nice discussion on smart pointers at [WayBack] Smart Pointers – Generics vrs non-generic implementastion – RTL and Delphi Object Pascal – Delphi-PRAXiS [en].

Conclusion from that:

  • many people think that reference counted interfaces are the same as Smart Pointers
    (basically Smart Pointers are the next level and of course they are based on reference counting)
  • there are a lot of Smart Pointer implementations, but few have a test suite, nor are optimised , nor easy to use
  • The combo Shared/IShared<T>/TShared<T> from Spring4D has all of the above advantages
  • in order to optmise Smart Pointer implementations, you really have to well know the effects of modern Delphi language constructs on the compiler in various target platforms

The discussion mentioned above includes both feature and speed comparisons.

I was a bit amazed that at CodeRage 2018, Marco Cantu introduced yet another smart pointer implementation: one worse than existing implementations, and one with only basic demonstration code, leaving out a test suite.

There have many posts on my blog about smart pointers (see the list below), but Spring4D smart pointer implementation has been around for such a long time that any well respected Delphi developer by now should use them. The source is at  Shared/IShared (search for {$REGION 'Shared smart pointer'} at the current repository).

This list below on my Smart Pointer related blog posts might not be fully complete, but at least mentions that by now you should be using Spring4D.

Some comments on the CodeRage 2018 demos

The code is at [WayBackgithub.com/marcocantu/DelphiSessions/tree/master/DelphiLanguageCodeRage2018

First a few generic comments:

  • With the exception of a few places, none of the demos really explain what they are after, nor indicate where the video or slides can be found. This is not OK for people after the conference bumping into them, and having to find their way back to the original meaning.
  • There are duplicated identical files in some of the directories. I think a seasoned Delphi evangelist should should emphasise on good naming and structure next to the actual topic they are after. Fresh Delphi users see these demos and – through their subconscious mind – think this is the organisation to go with. This leads to confirmation bias, which makes it harder to properly educate later on.
  • All of the demos use a VCL application plus form to show a language concept. That feels like a lot of overhead, and a missed chance to combine good Delphi language language use with for instance unit testing with DUnit or DUnitX, or even to condense each demo in a simple console application.
  • Having some random TForm## class names looks very sloppy, giving the wrong signal to developers.

Some comments per demo:

  1. 01_GenericMethodGenericForm.pas
    • I like the Result := GetTypeName (TypeInfo (T)); bit as it shows what the compiler can actually get from a generic type.
    • The demo would be much simpler when TGenericFunction was a record type instead of a class type.
    • No instance is needed: this could work with class methods (for the class type) or static class methods (for the record type) just as well.
    • The demo should emphasise that generic methods are still not possible with global methods that are outside of a type.
    • The demo should have shown more on the compiler type inference is shown. That would have given valuable insight on where the inference would have worked and where it would have failed.
    • For the “these are all wrong”, the compiler error should have been added as comment to the code so people can understand the errors they might run into when using generic methods.
  2. 02_SmartPointers:
    • SmartPointerClass.pas
      • Does not explain why it works
      • Given that there are no unit tests, and only a few test cases, you have no idea when it works or fails
    • SmartPointers_MainForm.pas
      • The “implicit create” demo does not indicate if procedure TSmartPointer<T>.Create; or constructor TSmartPointer<T>.Create(AValue: T); is called implicitly.
      • The demo parts should have shown which parts of SmartPointerClass.pas are being demoed. It looks like only shows creation and assignment to the TSmartPointer<T> based variable and how to obtain the content using the Value property, but actually class operator TSmartPointer<T>.Implicit(smart: TSmartPointer<T>): T; is shown as well.
  3. 03_WeakIntfRefWeakIntfRef_Form.pas
    • Having expanded Intf and Ref to Interface and Reference would have made the aim much clearer.
    • The demo name suggests  WeakIntfRefAttribute or WeakRefAttribute , but the attribute is in fact WeakAttribute which got [Archive.is] template documentation in XE3, but never an update on the real use, apart from a try to “document through a demo” (see below).
    • Only one aspect of WeakAttribute is shown, which fails without WeakAttribute as well: two objects having interface references to each other in the same scope will fail either way.
    • Contrary to the name, the demo also shows how to use UnsafeAttribute which got a [Archive.istemplate documentation in XE3, which [Archive.isgot slightly improved in Delphi XE4, only indicating the use of functions results, but nothing added ever since, nor explaining why/how it is considered dangerous.

      To set the unsafe attribute on a function result, use this syntax:

      [Result: Unsafe] function ReturnUnsafe: TObject;
      

      Warning: UnsafeAttribute should be only used outside the System unit. It is considered dangerous and its use is not recommended.

    •  Way better documentationWeakRefAttribute and UnsafeAttribute of is at[WayBack] Owl’s perspective: DelphiのRTLで定義済の属性 with a great Google translate below.
  4. 04_InlineVarsInlineVarsForm.pas
    • I like most demos
    • The inference demos should describe which type is actually inferred (for instance by showing the TypeInfo on the variable)
    • Demo Test8 does not explain if the const construct is permitted or not.
  5. 05_InlineVarSpeedInlineWeird.pas
    • This demo should actually explain the measurement differences.
    • In addition, this could have been way shorter by refactoring out all but the loop and the actual method calls into common code.
  6. 06_MoreTypeInferencemtiForm.pas
    • Since it is more advanced inference, the inferred types should be shown, especially since procedure TForm15.Button1Click(Sender: TObject); is clearly wrong indicating // dynamic array of objects, but the code demonstrating the inferred type  is confined to at least array of TComponent.
    • I have no idea why procedure TForm15.Button2Click(Sender: TObject); is in this demo: it has nothing to do with type inference.
    • Most demo’s show correct lifetime management using a try … finally construct, but procedure TForm15.Button2Click(Sender: TObject); does not. Odd.
  7. 07_BlockScopeBlockScope_Form.pas
    • This demo has the same SmartPointerClass.pas unit as 02_SmartPointers. Sloppy.
    • The demo shows that inline var declarations can be inside a beginend block, but does not show if implicit finalization of Smart Pointers is indeed local to that block, or extended to the method.
      So basically this does not show anything more than procedure Test5; // scope limited to local block in InlineVarsForm.pas.
      Luckily the thread mentioned above has a great example that does show, which I have quoted further below.
  8. 07_MissingAttribute:

Note I have not watched the above session, as the Embarcadero Academy Terms of Use (which are required to view most CodeRage 2018 sessions) disallow me to use any of that information on my blog: [WayBack/Archive.is] Terms of Use | Embarcadero Academy quote

Use License

  1. Permission is granted to temporarily download one copy of any downloadable materials on the School’s website for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not:
      1. modify or copy the materials;
      2. use the materials for any commercial purpose, or for any public display (commercial or non-commercial);
      3. attempt to decompile or reverse engineer any software contained on the School’s web site;
      4. remove any copyright or other proprietary notations from the materials; or
      5. transfer the materials to another person or ‘mirror’ the materials on any other server.
  2. This license shall automatically terminate if you violate any of these restrictions and may be terminated by Company at any time. Upon terminating your viewing of these materials or upon the termination of this license, you must destroy any downloaded materials in your possession whether in electronic or printed format.

Document through employee blogs outside your company

I think it is not a wise decision to document some new features only outside your own company domain, but on blogs of employees. That is not where your clients search first, so you should get that kind of documentation inside your domain as soon as possible after it gets published outside your domain.

So I like that it is there at all, but dislike it is not on the docwiki: [WayBack] New Delphi 10.3 Warning “Unknown Custom Attribute” (W1074).

Document through demo

Since Delphi XE6, [Archive.is] System.WeakAttribute – RAD Studio API Documentation however points to [Archive.is] RTL.AttributesAndRTTI Sample – RAD Studio Code Examples which leads to [WayBack] RAD Studio Demo Code / Code / [r2029] /branches/RadStudio_XE6/Object Pascal/RTL/AttributesAndRTTI.

As of Delphi RIO [WayBack] System.WeakAttribute – RAD Studio API Documentation, the link in [WayBack] RTL.AttributesAndRTTI Sample – RAD Studio Code Examples is broken ([WayBack] Page not found – SourceForge.net), as nobody bothered to create a new [WayBack] radstudiodemos branch on SourceForge, but the Delphi version rename hobbits did rename everything on the page from Tokyo to Rio.

This means the demo is now inside [WayBack] RADStudio10.3Demos/Object Pascal/RTL/AttributesAndRTTI/rtti_browser at master · Embarcadero/RADStudio10.3Demos · GitHub, which has no Rio in the URL name, so if the docwiki is fixed, the next mass search/replace there will miss it again.

Anyway: 10.3 Rio demos are at [WayBack] GitHub – Embarcadero/RADStudio10.3Demos: Delphi and C++Builder Demos for Embarcadero RAD Studio version 10.3 Rio

via: “{$WARN UNKNOWN_CUSTOM_ATTRIBUTE ERROR}” – Google Search

Block scope actually frees Smart Pointers at the end of the block

type
  TFoo = class(TInterfacedObject)
  public
    constructor Create;
    destructor Destroy; override;
  end;

constructor TFoo.Create;
begin
  inherited Create;
  Writeln('Foo Create');
end;

destructor TFoo.Destroy;
begin
  Writeln('Foo Destroy');
  inherited;
end;

procedure Test;
begin
  Writeln('procedure begin');
  Writeln('before inner scope begin');
  begin
    Writeln('after inner scope begin');
    var ref: IInterface := TFoo.Create;
    Writeln('before inner scope end');
  end;
  Writeln('after inner scope end');
  Writeln('something');
  Writeln('procedure end');
end;

Results with following output

 

procedure begin
before inner scope begin
after inner scope begin
Foo Create
before inner scope end
Foo Destroy
after inner scope end
something
procedure end

[WayBack] Owl’s perspective: DelphiのRTLで定義済の属性

Google translated is [Archive.is] Delphi RTL defined attributes:

September 3, 2014

Delphi RTL defined attributes

Although it is anattribute function included in the new RTTI added in Delphi 2010, although there are differences depending on the version of Delphi, there are some predefined attributes in RTL.However, there is no proper description in these help for these predefined attributes. I tried to find out that ( I have not examined XE 6 / XE 7 because it is not available, but I think that there is not much difference between existing ones and XE 5I confirmed about Delphi XE 6 / XE 7 / XE 8/10 Seattle / 10.1 Berlin).

    • System.Classes unit
      ComponentPlatformsAttribute [ComponentPlatforms(<params>)]
      [XE2..10.2 Tokyo] To specify the platform (Word value) that specifies the platform on which the component operates, specify the logical sum of the following constants defined in System.Classes
      • pidWin32 [XE2..10.2 Tokyo]
      • pidWin64 [XE2..10.2 Tokyo]
      • pidOSX32 [XE2..10.2 Tokyo]
      • pidiOSSimulator [XE3..10.2 Tokyo]
      • pidAndroid [XE3..10.2 Tokyo]
      • pidLinux32 [XE3..10.2 Tokyo]
      • pidiOSDevice32 [XE8..10.2 Tokyo], pidiOSDevice [XE3..10.2 Tokyo]
      • pidLinux64 [XE8..10.2 Tokyo]
      • pidWinNX32 [XE3..10.2 Tokyo]
      • pidWinIoT32 [XE8..10.2 Tokyo] (“Embedded IoT (Internet of Things) Windows w / Intel Galileo”)
      • pidiOSDevice64 [XE8..10.2 Tokyo]
      • pidWinARM [10 Seattle..10.2 Tokyo]
      • pidOSX64 [10.1 Berlin..10.2 Tokyo], pidOSXNX64 [10.1 Berlin..10.2 Tokyo]
      • pidLinux32Arm [10.1 Berlin..10.2 Tokyo]
      • pidLinux64Arm [10.1 Berlin..10.2 Tokyo]
      • pidAndroid64 [10.1 Berlin..10.2 Tokyo]
      TDefaultAttributeBase
      [XE 3 .. 10.2 Tokyo] DefaultAttribute / NoDefaultAttribute inherited from
      DefaultAttribute [Default(<param>)]
      [XE 3 … 10.2 Tokyo] Property storage designator Parameters equivalent to default have default values ​​(Boolean type, AnsiChar type, Char type, Integer type, Cardinal type, Int64 type, UInt 64 type, String type, Extended type One of them)
      NoDefaultAttribute [NoDefault]
      [XE3..10.2 Tokyo] Property storage designator Equivalent designation for nodefault
      StoredAttribute [Stored(<param>)]
      [XE3..XE7] Property storage Specify the name of a boolean value (False | True) or a storage handler (a method that returns a Boolean value without a Boolean property or parameter) for the specified parameter equivalent to stored (Moving to System unit after XE 8)
      ObservableMemberAttribute [ObservableMember(<param>)]
      [XE3..10.2 Tokyo] In LiveBinding compliant component, specify the member name used by the LiveBinding component to generate expressions as parameters
    • System.JSON.Serializers Unit
      JsonConverterAttribute [JsonConverter<param>]
      [10.2 Tokyo] Specifying the converter to be used with TJsonSerializer
      JsonIgnoreAttribute [JsonIgnore]
      [10.2 Tokyo] Specifying members to ignore with TJsonSerializer
      JsonNameAttribute [JsonName<param>]
      [10.2 Tokyo] Explicitly specify key names with TJsonSerializer
      JsonInAttribute [JsonIn]
      [10.2 Tokyo] When TJsonMemberSerialization.In is specified as the parameter of the JsonSerialize attribute, it is targeted for serialization / deserialization by TJsonSerializer
      JsonObjectHandlingAttribute [JsonObjectHandling<param>]
      [10.2 Tokyo] Specify handling of members of class type when deserializing with TJsonSerializer Specify one of the following values ​​from the enumeration type TJsonObjectHandling
      • Auto [10.2 Tokyo]
      • Reuse [10.2 Tokyo]
      • Replace [10.2 Tokyo]
      JsonObjectOwnership [JsonObjectOwnership<param>]
      [10.2 Tokyo] Specify handling of the original instance when class type member is generated when deserializing with TJsonSerializer Specify one of the following values ​​from the enumeration type TJsonObjectOwnership
      • Auto [10.2 Tokyo]
      • Owned [10.2 Tokyo]
      • NotOwned [10.2 Tokyo]
      JsonSerializeAttribute [JsonSerialize<param>]
      [10.2 Tokyo] Specify one of the following values ​​from the enumeration type TJsonMemberSerialization as the specified parameter of the member to be serialized / deserialized by TJsonSerializer
      • Fields [10.2 Tokyo]
      • Public [10.2 Tokyo]
      • In [10.2 Tokyo]
    • Data.DBXJSONReflect unit (replaced by REST.JsonReflect unit in XE 5)
      JsonReflect [JsonReflect(<params>)]
      [XE..10.2 Tokyo] Parameters customizing the behavior when marshalling / unmarshalling objects in JSON format with TJSONMarshal / TJSONUnMarshal include the converter type, reverter type, interceptor type, population customizer type, marshaling Specify whether to automatically release generated intermediate objects
      JSONBooleanAttribute
      [XE..10.2 Tokyo] JSONMarshalled / JSONOwned to inherit from
      JSONMarshalled [JSONMarshalled(<param>)]
      [XE..10.2 Tokyo] The parameter (False | True) specifies whether the field and type are subject to marshalling / unmarshalling
      JSONOwned [JSONOwned(<param>)]
      The parameter (False | True) specifies whether [XE..10.2 Tokyo] field is released when unmarshalling
    • REST.JsonReflect unit
      JsonReflect(XE5)/JsonReflectAttribute(XE6以降) [JsonReflect]
      [XE5..10.2 Tokyo] Refer to Data.DBXJSONReflect.JsonReflect (the class name is changed in XE 6)
      JSONBooleanAttribute [JSONBoolean]
      [XE5] Refer to Data.DBXJSONReflect.JSONBooleanAttribute (Moved to REST.Json.Types unit after XE6)
      JSONMarshalled [JSONMarshalled]
      [XE5] Refer to Data.DBXJSONReflect.JSONMarshalled (Moved to REST.Json.Types unit after XE 6)
      JSONOwned [JSONOwned]
      [XE5] Refer to Data.DBXJSONReflect.JSONOwned (move to REST.Json.Types unit after XE 6)
    • Macapi.ObjectiveC unit
      MethodNameAttribute [MethodName]
      [XE4..10.2 Tokyo] For the Objective-C method defined in Delphi, specify the original method name on Objective-C
    • Androidapi.JNIBridge unit
      JavaSignatureAttribute [JavaSignature]
      [XE5..10.2 Tokyo] Specify the original class name on Android for Android (JNI) class (interface) defined in Delphi
  • System.Win.WinRT Unit
    WinRTClassNameAttribute [WinRTClassName]
    [10 Seattle … 10.2 Tokyo] Specify the original class name on WinRT for the interface of WinRT API defined in Delphi

Differences between versions of Delphi:

  • In Delphi 2010 there are no predefined attributes used in the system.
  • Delphi XE defines TRoleAuth in relation to DataSnap and JSON ... for JSON reflection in DBX relation.
  • In Delphi XE 2, ComponentPlatformsAttribute was defined for multi platform support.
  • WeakAttribute / UnsafeAttribute / RefAttribute for ARC support in Delphi XE 3, TDefaultAttributeBase / DefaultAttribute / NoDefaultAttribute / StoredAttribute for migration from a future old RTTI to a new RTTI, and ObservableMemberAttribute for LiveBinding correspondence were respectively defined.
  • In Delphi XE 4, MethodNameAttribute was defined as iOS support.
  • In Delphi XE 5, since reflection of JSON was separated from DBX, JSON … of Data.DBXJSONReflect was redefined for REST.JsonReflect, and JavaSignatureAttribute was defined as Android correspondence.
  • In Delphi XE 6, REST.JsonReflect.JsonReflect was renamed to REST.JsonReflect.JsonReflectAttribute and the definition of Json ... other than JsonReflectAttribute moved from the REST.JsonReflect unit to the REST.Json.Types unit.
  • In Delphi XE 7, the definition of StoredAttribute moved from the System.Classes unit to the System unit, and ResourceSuffixAttribute / ResourceNameAttribute / EndpointNameAttribute was defined for EMS.
  • In Delphi XE 8 HPPGENAttribute and HFAAttribute were defined.
  • In Delphi 10 Seattle AlignAttribute and WinRTClassNameAttribute were defined.
  • In Delphi 10.1 Berlin, the [weak][unsafe][volatile] attribute is now supported by all compilers (NEXTGEN compiler only before 10 Seattle). We also defined TResourceCustomAttribute / EndPointRequestSummaryAttribute / EndPointRequestParameterAttribute / EndPointResponseDetailsAttribute / EndPointObjectsYAMLDefinitionsAttribute / EndPointObjectsJSONDefinitionsAttribute for custom API documents used in retrieving EMS API resources .
  • In Delphi 10.2 Tokyo, System.JSON.Serializers unit was added to define Json ... related to JSON serialization, and AllowAnonymousTenantAttribute was defined for multi-tenant support of EMS. As for the System.JSON.Serializers unit, since it is not officially documented at the present time (April 10, 2007), it is a change that seems not to be written in docwiki of Lyna ‘s Delphi 10.2 Tokyo. – Powerfully , how to use TJsonSerializer. – Powerfully , see the practical example of TJsonSerializer – full power .

If you want to specify an attribute as a return value of a method like Unsafe attribute, add [Result: <attr>] before the method declaration,

[Result: Unsafe]function Foo: TObject;

I will do as follows.

  • 2015/06/19 Addendum: Added information on Delphi XE 6 / XE 7 / XE 8.
  • 2015/10/05 Addendum: Added information on Delphi 10 Seattle.
  • 2016/05/02 Addendum: Added information on Delphi 10.1 Berlin.
  • 2017/04/10 Addendum: Added information on Delphi 10.2 Tokyo. I quoted a lot from Lyna’s point . Thank you very much.

–jeroen


Viewing all articles
Browse latest Browse all 1440

Trending Articles