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

Go to full version