Forum > FPC development
Custom attributes on properties and methods: how?
(1/1)
Ștefan-Iulian Alecu:
Hello! I have been interested in FPC trunk as of late for a project I am doing (Version=3.3.1-18324-g831e6c6d75-dirty, Windows 11 64 bit, although I don't believe the hash is as important here). I was experimenting with custom attributes, hoping they'd be at least somewhat usable. So I made this example:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program AttributeDateDemo; {$mode objfpc}{$H+}{$modeswitch prefixedattributes} uses SysUtils, Rtti; type DateTimeAttribute = class(TCustomAttribute) private FValue: TDateTime; public constructor Create(const RawDate: String); property Value: TDateTime read FValue; end; constructor DateTimeAttribute.Create(const RawDate: String);var TempDate: TDateTime;begin ShortDateFormat := 'yyyy/mm/dd'; DateSeparator := '/'; if not TryStrToDate(RawDate, TempDate) then raise Exception.CreateFmt('Invalid date: "%s"', [RawDate]); FValue := TempDate;end; type [DateTimeAttribute({$I %DATE%})] TFoo = class private FProp: String; public [DateTime({$I %DATE%})] procedure DoSomething; [DateTime({$I %DATE%})] property MyProp: String read FProp write FProp; end; procedure TFoo.DoSomething;begin Writeln('Doing something...');end; procedure PrintAttributes(AType: TRttiType);var Attr: TCustomAttribute; Meth: TRttiMethod; Prop: TRttiProperty;begin Writeln('Class attributes:'); for Attr in AType.GetAttributes do if Attr is DateTimeAttribute then Writeln(' ', FormatDateTime('dd.mm.yyyy', DateTimeAttribute(Attr).Value)); Writeln('Method attributes:'); for Meth in AType.GetMethods do if Meth.Name = 'DoSomething' then for Attr in Meth.GetAttributes do if Attr is DateTimeAttribute then Writeln(' ', FormatDateTime('dd.mm.yyyy', DateTimeAttribute(Attr).Value)); Writeln('Property attributes:'); for Prop in AType.GetProperties do if Prop.Name = 'MyProp' then for Attr in Prop.GetAttributes do if Attr is DateTimeAttribute then Writeln(' ', FormatDateTime('dd.mm.yyyy', DateTimeAttribute(Attr).Value));end; var Ctx: TRttiContext; Typ: TRttiType;begin Typ := Ctx.GetType(TFoo); PrintAttributes(Typ); Readln;end. Ignoring ShortDateFormat and DateSeparator there, which aren't the main point, I was expecting to see the same date on all attributes, however I got this instead:
--- Code: Text [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---Class attributes: 02.08.2025Method attributes:Property attributes: which caught me by surprise. Am I doing something wrong in the PrintAttributes procedure?
P.S. Is there a way to have attributes on parameters too? An example would be:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---TMyClass = class class procedure MyMethod([NotNull] Address: Pointer); static;end; which is at least possible in Delphi, after someone showed me how they would access it. My use case would be to have [NonNull] and [NonEmpty] attributes. I don't see a mention of them on the Free Pascal wiki page on custom attributes, and I have reasons to believe that's not in testing yet. Maybe it's a missing feature currently? :)
If you consider this topic to not be appropriate for this board, feel free to move it. Thanks for your attention!
PascalDragon:
--- Quote from: Ștefan-Iulian Alecu on August 02, 2025, 02:10:28 pm ---Hello! I have been interested in FPC trunk as of late for a project I am doing (Version=3.3.1-18324-g831e6c6d75-dirty, Windows 11 64 bit, although I don't believe the hash is as important here). I was experimenting with custom attributes, hoping they'd be at least somewhat usable.
--- End quote ---
The visibilities for custom attributes for fields, methods and properties are still set to None, so you need to manually enable them by adding {$RTTI EXPLICIT PROPERTIES([vcPublic])} after the uses-clause.
--- Quote from: Ștefan-Iulian Alecu on August 02, 2025, 02:10:28 pm ---P.S. Is there a way to have attributes on parameters too? An example would be:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---TMyClass = class class procedure MyMethod([NotNull] Address: Pointer); static;end; which is at least possible in Delphi, after someone showed me how they would access it. My use case would be to have [NonNull] and [NonEmpty] attributes. I don't see a mention of them on the Free Pascal wiki page on custom attributes, and I have reasons to believe that's not in testing yet. Maybe it's a missing feature currently? :)
--- End quote ---
These are currently not supported.
Ștefan-Iulian Alecu:
--- Quote from: PascalDragon on August 03, 2025, 02:05:42 pm ---
--- Quote from: Ștefan-Iulian Alecu on August 02, 2025, 02:10:28 pm ---Hello! I have been interested in FPC trunk as of late for a project I am doing (Version=3.3.1-18324-g831e6c6d75-dirty, Windows 11 64 bit, although I don't believe the hash is as important here). I was experimenting with custom attributes, hoping they'd be at least somewhat usable.
--- End quote ---
The visibilities for custom attributes for fields, methods and properties are still set to None, so you need to manually enable them by adding {$RTTI EXPLICIT PROPERTIES([vcPublic])} after the uses-clause.
--- End quote ---
Thank you so much. Perhaps this should be documented in the wiki page, at least so that people know it is the same syntax as Delphi.
--- Quote from: PascalDragon on August 03, 2025, 02:05:42 pm ---These are currently not supported.
--- End quote ---
Oh well. Is there a way to at least "fake" them? Perhaps something like
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type TMyClass = class [NotNull('Address')] class procedure MyMethod(Address: Pointer); static; end; but it's uncertain to me how I could wire this up so it works. I tried this:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program AttributeNotNullDemo; {$mode objfpc}{$H+}{$modeswitch prefixedattributes} uses SysUtils, Rtti; {$RTTI EXPLICIT METHODS([vcPublic])} type NotNullAttribute = class(TCustomAttribute) private FParamName: String; public constructor Create(const AParamName: String); property ParamName: String read FParamName; end; type TMyClass = class [NotNull('Address')] class procedure MyMethod(Foo: Pointer); static; end; { NotNullAttribute } constructor NotNullAttribute.Create(const AParamName: String);begin FParamName := AParamName;end; class procedure TMyClass.MyMethod(Foo: Pointer);begin if Foo = nil then raise Exception.Create('Address must not be nil'); Writeln('Address OK!');end; procedure PrintMethodAttributes(AType: TRttiType);var Meth: TRttiMethod; Attr: TCustomAttribute;begin for Meth in AType.GetMethods do if Meth.Name = 'MyMethod' then begin Writeln('Attributes on method MyMethod:'); for Attr in Meth.GetAttributes do if Attr is NotNullAttribute then Writeln(' NotNull: ', NotNullAttribute(Attr).ParamName); end;end; var Ctx: TRttiContext; Typ: TRttiType; i: Integer;begin Typ := Ctx.GetType(TMyClass); PrintMethodAttributes(Typ); i := 10; try TMyClass.MyMethod(@i); TMyClass.MyMethod(nil); except on E: Exception do Writeln('Runtime check: ', E.Message); end; Readln;end. but it totally ignores the name. I am fine with this being a numerical index as well.
Thaddy:
Did you compile the class with {$M+} ?
PascalDragon:
--- Quote from: Ștefan-Iulian Alecu on August 03, 2025, 03:29:27 pm ---
--- Quote from: PascalDragon on August 03, 2025, 02:05:42 pm ---
--- Quote from: Ștefan-Iulian Alecu on August 02, 2025, 02:10:28 pm ---Hello! I have been interested in FPC trunk as of late for a project I am doing (Version=3.3.1-18324-g831e6c6d75-dirty, Windows 11 64 bit, although I don't believe the hash is as important here). I was experimenting with custom attributes, hoping they'd be at least somewhat usable.
--- End quote ---
The visibilities for custom attributes for fields, methods and properties are still set to None, so you need to manually enable them by adding {$RTTI EXPLICIT PROPERTIES([vcPublic])} after the uses-clause.
--- End quote ---
Thank you so much. Perhaps this should be documented in the wiki page, at least so that people know it is the same syntax as Delphi.
--- End quote ---
If someone thinks that's a useful information for that article they can add it. It's a public wiki after all. It's however not the official documentation. And as main has no upcoming release currently there simply is no documentation for that feature.
--- Quote from: Ștefan-Iulian Alecu on August 03, 2025, 03:29:27 pm ---
--- Quote from: PascalDragon on August 03, 2025, 02:05:42 pm ---These are currently not supported.
--- End quote ---
Oh well. Is there a way to at least "fake" them?
--- End quote ---
You can fake them as much as you want, the compiler won't support you there and this will not change. Either attributes for parameter will come or nothing along that line will come, simple as that.
Navigation
[0] Message Index