From pascaldragon at googlemail.com Sat Jul 13 00:09:58 2019 From: pascaldragon at googlemail.com (Sven Barth) Date: Sat, 13 Jul 2019 00:09:58 +0200 Subject: [fpc-announce] Initial support for Custom Attributes Message-ID: <89ef9502-e1c8-523a-0c86-b3d51ba2e79a@googlemail.com> Hello together! Today FPC has finally gained initial support for Custom Attributes. The work had initially been done by Joost van der Sluis almost 6 years ago and Svetozar Belic had adjusted the code for trunk. So thank you both for that work even if it took quite some time until it was finally integrated into trunk. What are attributes? Custom Attributes allow you to decorate (currently) type definitions and published properties of classes with additional metadata that can be queried using the RTTI. What can attributes be used for? You can use them to mark classes with the name of its corresponding database table or ithe base path for a web service class. How are attributes declared? Attributes are simply classes that descend from the new System type TCustomAttribute. The important part are the constructors of the class. These can be used to pass additional parameters to the attribute (like the table name or path). How are attributes used? Attributes are bound to a type or property by using one or multiple attribute clauses in front of the type or property. For types it must be a type definition (e.g. a class, a record, an enum, etc.) or a unique type redeclaration (e.g. "TLongInt = type LongInt"). Mere type renames (e.g. "TLongInt = LongInt") are not allowed. Attribute clauses are only available if the new modeswitch PREFIXEDATTRIBUTES is set which is the default in mode Delphi and DelphiUnicode. The syntax of a attribute clause is the following: ATTRIBUTECLAUSE::='[' ATTRIBUTELIST ']' ATTRIBUTELIST::=ATTRIBUTE [, ATTRIBUTELIST ] ATTRIBUTE::=IDENTIFIER [ ( PARAMLIST ) ] PARAMLIST::=CONSTEXPR [, PARAMLIST ] The IDENTIFIER is either the name of the attribute class as is or the attribute class' name can end in "Attribute" (casing irrelevant) and then the name may be used without the "Attribute" suffix. Take the following example: === code begin === program tcustomattr; {$mode objfpc}{$H+} {$modeswitch prefixedattributes} type   TMyAttribute = class(TCustomAttribute)     constructor Create;     constructor Create(aArg: String);     constructor Create(aArg: TGUID);     constructor Create(aArg: LongInt);   end;   {$M+}   [TMyAttribute]   TTestClass = class   private     fTest: LongInt;   published     [TMyAttribute('Test')]     property Test: LongInt read fTest;   end;   {$M-}   [TMyAttribute(1234)]   [TMy('Hello World')]   TTestEnum = (     teOne,     teTwo   );   [TMyAttribute(IInterface), TMy(42)]   TLongInt = type LongInt; constructor TMyAttribute.Create; begin end; constructor TMyAttribute.Create(aArg: String); begin end; constructor TMyAttribute.Create(aArg: LongInt); begin end; constructor TMyAttribute.Create(aArg: TGUID); begin end; begin end. === code end === Querying attributes: Attributes can be accessed by both the TypInfo and Rtti units. For the TypInfo unit the ways to access attributes are as follows: For types: - use the AttributesTable field in TTypeData - use GetAttributeTable on a PTypeInfo - use GetAttribute on the attribute table together with an index to get a TCustomAttribute instance For properties: - use the AttributesTable of TPropInfo - use GetAttribute on the attribute table together with a nindex to get a TCustomAttribute instance - use GetPropAttribute on the PPropInfo together with an index to get a TCustomAttribute instnace For the Rtti unit the ways to access attributes are as follows: For types: - use GetAttributes on the TRttiType of the type in question For properties: - use GetAttributes on the TRttiProperty of the property in question How is the compatibility of the attributes feature: The feature itself is Delphi compatible except FPC is much more unforgiving regarding unbound properties: if the attribute class is not known or the attribute clauses are not bound to a valid type or property the compiler will generate an error. The RTTI however is not considered Delphi compatible, but it covers the same functionality. Contrary to Delphi which uses Invoke to create the attribute instance FPC uses a constructor function which has the advantage that it works on systems that don't have full Invoke support. Additionally using the PREFIXEDATTRIBUTES modeswitch disables the directive clauses for functions, methods and procedure/method types: The following is not allowed anymore with the modeswitch enabled: === code begin === procedure Test; [cdecl]; begin end; === code end === Just in case: this feature won't be part of 3.2. The wiki pages New Features Trunk and User Changes Trunk will be updated soon with the new information. Regards, Sven