[fpc-announce] Initial support for Custom Attributes
Sven Barth
pascaldragon at googlemail.com
Sat Jul 13 00:09:58 CEST 2019
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
More information about the fpc-announce
mailing list