Sign in Join
Wiki copytemporary

Custom Attributes: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Custom Attributes

OldNewLine
1+Custom Attributes currently allow you to decorate type definitions and published properties of classes with additional metadata that can be queried using RTTI (runtime type information).
2+
3+**Currently (as of October 2020) the feature is available only in FPC trunk (aka 3.3.1)**
4+
5+## What can attributes be used for?
6+
7+An attribute is used to associate specific metadata with a class. For example you can use an attribute to mark a class with the name of its corresponding database table, or to annotate a web service class with a string identifying its base path.
8+
9+## How are attributes declared?
10+
11+Attributes are simply classes that descend from the new System type TCustomAttribute. The constructors of such TCustomAttribute descendants are the most important feature to implement since they are used to pass additional parameters to the attribute (such as the associated database table name, or the base path in the above examples).
12+
13+**Important**: If you want to use your attribute type without any arguments then you *must* declare a parameterless constructor yourself as the one of *TCustomAttribute* is private.
14+
15+## How are attributes used?
16+
17+Attributes are bound to a type or property by specifying at least one attribute clause ahead of the type or property. For a type the attribute is specified in the type definition (such as a class, record, or enum declaration) or in a unique type redeclaration (e.g. "TLongInt = type LongInt"). Mere type renames (e.g. "TLongInt = LongInt") are not allowed.
18+
19+Attribute clauses are only available if the new modeswitch PREFIXEDATTRIBUTES is set, which is the default in mode Delphi and DelphiUnicode.
20+
21+The syntax of an attribute clause is as follows:
22+
23+`ATTRIBUTECLAUSE::='[' ATTRIBUTELIST ']'`\
24+`ATTRIBUTELIST::=ATTRIBUTE [, ATTRIBUTELIST ]`\
25+`ATTRIBUTE::=IDENTIFIER [ ( PARAMLIST ) ]`\
26+`PARAMLIST::=CONSTEXPR [, PARAMLIST ]`
27+
28+The IDENTIFIER is the name of the attribute class. If you name the attribute class to end with "Attribute" (the case of the "attribute" suffix is immaterial) then the name may be used subsequently without the "Attribute" suffix. So TMyAttribute and TMy are equivalent alternatives in the following example:
29+
30+```delphi
31+
32+program tcustomattr;
33+
34+{$mode objfpc}{$H+}
35+{$modeswitch prefixedattributes}
36+
37+type
38+ TMyAttribute = class(TCustomAttribute)
39+ constructor Create;
40+ constructor Create(aArg: String);
41+ constructor Create(aArg: TGUID);
42+ constructor Create(aArg: LongInt);
43+ end;
44+
45+ {$M+}
46+ [TMyAttribute]
47+ TTestClass = class
48+ private
49+ fTest: LongInt;
50+ published
51+ [TMyAttribute('Test')]
52+ property Test: LongInt read fTest;
53+ end;
54+ {$M-}
55+
56+ [TMyAttribute(1234)]
57+ [TMy('Hello World')]
58+ TTestEnum = (
59+ teOne,
60+ teTwo
61+ );
62+
63+ [TMyAttribute(IInterface), TMy(42)]
64+ TLongInt = type LongInt;
65+
66+constructor TMyAttribute.Create;
67+begin
68+end;
69+
70+constructor TMyAttribute.Create(aArg: String);
71+begin
72+end;
73+
74+constructor TMyAttribute.Create(aArg: LongInt);
75+begin
76+end;
77+
78+constructor TMyAttribute.Create(aArg: TGUID);
79+begin
80+end;
81+
82+begin
83+
84+end.
85+```
86+
87+### Querying attributes
88+
89+Attributes can be accessed by both the TypInfo and Rtti units.
90+
91+For the TypInfo unit the ways to access attributes are as follows:
92+
93+For types:
94+
95+- use the AttributesTable field in TTypeData
96+- use GetAttributeTable on a PTypeInfo
97+- use GetAttribute on the attribute table together with an index to get a TCustomAttribute instance
98+
99+For properties:
100+
101+- use the AttributesTable of TPropInfo
102+- use GetAttribute on the attribute table together with an index to get a TCustomAttribute instance
103+- use GetPropAttribute on the PPropInfo together with an index to get a TCustomAttribute instance
104+
105+For the Rtti unit the ways to access attributes are as follows:
106+
107+For types:
108+
109+- use GetAttributes on the TRttiType of the type in question
110+
111+For properties:
112+
113+- use GetAttributes on the TRttiProperty of the property in question
114+
115+## The attributes feature's compatibility with Delphi
116+
117+The feature itself is Delphi compatible with the proviso that 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.
118+
119+FPC's RTTI is not considered Delphi compatible. However it covers the same functionality. In contrast to Delphi (which uses Invoke to create the attribute instance) FPC uses a constructor function. FPC's implementation has the advantage of working on systems that don't have full Invoke support.
120+
121+Additionally using the PREFIXEDATTRIBUTES modeswitch disables the directive clauses for functions, methods and procedure/method types. The following is not allowed any more with the PREFIXEDATTRIBUTES modeswitch enabled:
122+
123+```delphi
124+procedure Test; [cdecl];
125+begin
126+end;
127+```
128+
129+## Complete example
130+
131+```Delphi
132+program testattributes;
133+{
134+ This is a simple example on how to use custom attributes.
135+ The class uses a custom attribute to retrieve a static date
136+ at program start-up. This is just for the purpose of demo.
137+ This demo's:
138+ - Creation
139+ - Decoration
140+ - Retrieval
141+}
142+
143+{$mode delphi}{$H+}{$M+}
144+{$warn 5079 off} { turn warning experimental off }
145+uses
146+ sysutils, typinfo, rtti, classes;
147+
148+type
149+ {A custom attribute to decorate a class with a certain date }
150+ ADateTimeAttribute = class(TCustomAttribute)
151+ private
152+ FArg:TDateTime;
153+ public
154+ { Just to show a Custom Attribute can have mutiple constructors }
155+ constructor Create(const aArg: TDateTime);overload;
156+ { We can use the predefined %DATE% compiler include
157+ In the context of a custom attribute we need a
158+ constant expression for the default parameter }
159+ constructor Create(const aArg: String = {$I %DATE%});overload;
160+ published
161+ property Date:TDateTime read Farg;
162+ end;
163+
164+ { A datetime class that is decorated with our custom attribute }
165+ { Note you can leave out 'Attribute', the compiler resolves it }
166+ { [ADateTime(21237.0)] uses first constructor,displays some date in 1958 }
167+
168+ {This calls the second constructor }
169+ [ADateTime]
170+ TMyDateTimeClass = class
171+ private
172+ FDateTime:TDateTime;
173+ public
174+ constructor create;
175+ published
176+ [ADateTime]
177+ property Day:TDateTime read FDatetime write FdateTime;
178+ end;
179+
180+ constructor ADateTimeAttribute.Create(const aArg: TDateTime);
181+ begin
182+ FArg := aArg;
183+ end;
184+
185+ constructor ADateTimeAttribute.Create(const aArg: string );
186+ var
187+ MySettings:Tformatsettings;
188+ begin
189+ { set up the date format according to how
190+ the compiler formats %DATE% include }
191+ MySettings :=DefaultFormatSettings;
192+ MySettings.ShortDateFormat:='yyyymmdd';
193+ MySettings.DateSeparator :='/';
194+ { Now convert }
195+ FArg := StrToDateTime(aArg, MySettings);
196+ end;
197+
198+ { We query the rtti to set the value }
199+ constructor TMyDateTimeClass.Create;
200+ var
201+ Context : TRttiContext;
202+ AType : TRttiType;
203+ Attribute : TCustomAttribute;
204+ begin
205+ inherited;
206+ Context := TRttiContext.Create;
207+ try
208+ AType := Context.GetType(typeinfo(TMyDateTimeClass));
209+ for Attribute in AType.GetAttributes do begin
210+ if Attribute is ADateTimeAttribute then
211+ FDateTime := ADateTimeAttribute(Attribute).Date;
212+ end;
213+ finally
214+ Context.Free
215+ end;
216+ end;
217+
218+
219+var
220+ Test:TMyDateTimeClass;
221+begin
222+ Test := TMyDateTimeClass.Create;
223+ try
224+ writeln('Compile date is :',DateTimeToStr(Test.Day));
225+ finally
226+ test.free;
227+ end;
228+end.
229+```
230+
231+## See Also
232+
233+- [[FPC New Features Trunk]]
234+- <http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Attributes> - Delphi Attributes reference
235+- <http://delphi.about.com/od/oopindelphi/a/delphi-attributes-understanding-using-attributes-in-delphi.htm> Delphi Tutorial on property attributes
236+
237+---
238+*From the Free Pascal wiki, page "Custom Attributes", as it stood on 2022-12-30 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
239+Category: FPC

Back to history