Recent

Author Topic: Generics as PUBLISHED impossible?  (Read 3717 times)

YetAnotherDelphiLeaver

  • Newbie
  • Posts: 4
Generics as PUBLISHED impossible?
« on: May 31, 2024, 11:52:24 am »
Hello,
I have implemented a class that allows the thread-safe read/write of any type of variable.

Unfortunately, while the code works just fine under Delphi, it cannot be compiled under Lazarus 3.4.
The error message implies that Generics can not be published. Is that really correct or am I missing some workaround?

 
Code: [Select]
TYPE TThreadSafeValue<T>
       = CLASS
           PROTECTED
             Synchronizer:TMultiReadExclusiveWriteSynchronizer;
             CriticalSectionChange:TCriticalSection;

             FUNCTION  ReadValue:T;
             PROCEDURE WriteValue(NewValue:T);

             FUNCTION  ReadDefaultValue:T;
             PROCEDURE WriteDefaultValue(NewDefault:T);

             FUNCTION GetChanged:Boolean;
           PRIVATE
             LocalValue:T;
             LocalDefault:T;
             LocalChanged:Boolean;
           PUBLIC
             CONSTRUCTOR Create;
             DESTRUCTOR  Destroy; OVERRIDE;

             PROCEDURE   SetDefault;
           PUBLISHED
             PROPERTY Value       :T  READ ReadValue        WRITE WriteValue;
             PROPERTY DefaultValue:T  READ ReadDefaultValue WRITE WriteDefaultValue;
             PROPERTY Changed:Boolean READ GetChanged;
       END;

« Last Edit: January 13, 2025, 11:31:01 am by YetAnotherDelphiLeaver »

Thaddy

  • Hero Member
  • *****
  • Posts: 16538
  • Kallstadt seems a good place to evict Trump to.
Re: Generics as PUBLISHED impossible?
« Reply #1 on: May 31, 2024, 01:28:27 pm »
Unfortunately, while the code works just fine under Delphi, it cannot be compiled under Lazarus.
That is a bit liberal with the truth, because your code won't compile in Delphi at all.
(BTW: turn capslock off when writing code.)
« Last Edit: May 31, 2024, 01:29:58 pm by Thaddy »
But I am sure they don't want the Trumps back...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12042
  • FPC developer.
Re: Generics as PUBLISHED impossible?
« Reply #2 on: May 31, 2024, 02:46:32 pm »
It compiles in Delphi Seattle here if I put it in a console program with uses. {$M+} and class complete it. And it doesn't compile with FPC 3.3.1 as of today with the named error.

But if this is correct and why, I don't know.
« Last Edit: May 31, 2024, 02:49:19 pm by marcov »

Thaddy

  • Hero Member
  • *****
  • Posts: 16538
  • Kallstadt seems a good place to evict Trump to.
Re: Generics as PUBLISHED impossible?
« Reply #3 on: May 31, 2024, 05:14:07 pm »
I have obviously need to test some more. Report back later.
Edit: mentioning the Delphi version would have been helpful.
« Last Edit: May 31, 2024, 06:45:05 pm by Thaddy »
But I am sure they don't want the Trumps back...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12042
  • FPC developer.
Re: Generics as PUBLISHED impossible?
« Reply #4 on: May 31, 2024, 06:52:33 pm »
I said Seattle. (which is 10.0)

Thaddy

  • Hero Member
  • *****
  • Posts: 16538
  • Kallstadt seems a good place to evict Trump to.
Re: Generics as PUBLISHED impossible?
« Reply #5 on: June 01, 2024, 11:18:28 am »
Yes, it compiles, but what is the point to have those properties published? I can't see a use-case. Except for streaming, but that requires to derive from Tpersistent to be of any use. (Because {$M+} state does not work very well if not derived from Tpersistent, see manual )
But I am sure they don't want the Trumps back...

PascalDragon

  • Hero Member
  • *****
  • Posts: 5862
  • Compiler Developer
Re: Generics as PUBLISHED impossible?
« Reply #6 on: June 02, 2024, 05:28:38 pm »
TYPE TThreadSafeValue<T>

Please use [code][/code]-tags for source code to avoid the forum software interpreting it and having a nicer look overall.

Yes, it compiles, but what is the point to have those properties published? I can't see a use-case. Except for streaming, but that requires to derive from Tpersistent to be of any use. (Because {$M+} state does not work very well if not derived from Tpersistent, see manual )

If one implements one's own streaming then one doesn't need TPersistent. Anything that TPersistent does can be achieved by any other type as well, cause there is no TPersistent-specific compiler magic.

Also for use cases: Writing the code for the base class simply once. As long as a type suitable for published is used when specializing it, it would work.

@YetAnotherDelphiLeaver: please report a bug.

YetAnotherDelphiLeaver

  • Newbie
  • Posts: 4
Re: Generics as PUBLISHED impossible?
« Reply #7 on: January 13, 2025, 11:05:55 am »
That is a bit liberal with the truth, because your code won't compile in Delphi at all.

Well, it surely compiles and works with Delphi XE2.

Quote
(BTW: turn capslock off when writing code.)

There was no capslock being active during the writing of that post  :) ; I am simply used to capitalize all reserved words for better readability.
May not be required anymore in modern editors with automatic syntax highlighting, but for sure was helpful with TurboPascal 5.5 under MS-DOS 4.11 when I started coding.
« Last Edit: January 13, 2025, 11:32:00 am by YetAnotherDelphiLeaver »

YetAnotherDelphiLeaver

  • Newbie
  • Posts: 4
Re: Generics as PUBLISHED impossible?
« Reply #8 on: January 13, 2025, 11:21:20 am »
After almost one year of trying different options I haven't still found a solution for using FPC generics as described (except, of course, the obvious solution to copy&paste the code snippet about 10 times for all possible variable types that I need, which is not really nice and in principle not good for code maintenance).

The idea of this code is simply to use something very similar of global variables, but with safety against unsynchronized access of many different threads.

Examples could be any global parameter in a program, that is accessed by multiple threads and forms, such as GUI colors. Performance is not critical, since those parameters may be accessed only seldomly (upon OnClock events, in particular).

Code: [Select]
TYPE TThreadSafeValue<T>
       = CLASS
           PROTECTED
             Synchronizer:TMultiReadExclusiveWriteSynchronizer;
             CriticalSectionChange:TCriticalSection;

             FUNCTION  ReadValue:T;
             PROCEDURE WriteValue(NewValue:T);

             FUNCTION  ReadDefaultValue:T;
             PROCEDURE WriteDefaultValue(NewDefault:T);

             FUNCTION GetChanged:Boolean;
           PRIVATE
             LocalValue:T;
             LocalDefault:T;
             LocalChanged:Boolean;
           PUBLIC
             CONSTRUCTOR Create;
             DESTRUCTOR  Destroy; OVERRIDE;

             PROCEDURE   SetDefault;
           PUBLISHED
             PROPERTY Value       :T  READ ReadValue        WRITE WriteValue;
             PROPERTY DefaultValue:T  READ ReadDefaultValue WRITE WriteDefaultValue;
             PROPERTY Changed:Boolean READ GetChanged;
       END; 


 CONSTRUCTOR TThreadSafeValue<T>.Create;
  BEGIN
    INHERITED;

    Synchronizer:=TMultiReadExclusiveWriteSynchronizer.Create;
    CriticalSectionChange:=TCriticalSection.Create;
    // initialize MyValue with default value of type
    LocalValue:=Default(T);
  END;


 DESTRUCTOR  TThreadSafeValue<T>.Destroy;
  BEGIN
    Synchronizer.Destroy;
    CriticalSectionChange.Destroy;

    INHERITED;
  END;


 FUNCTION  TThreadSafeValue<T>.ReadValue;
  VAR Dummy:T;
  BEGIN
    // Read MyValue safely into Dummy
    Synchronizer.BeginRead;
    Dummy:=LocalValue;
    Synchronizer.EndRead;
    // and return as result
    Result:=Dummy;
  END;


 PROCEDURE TThreadSafeValue<T>.WriteValue(NewValue:T);
  VAR OldValue:T;
  BEGIN
    Synchronizer.BeginWrite;
    OldValue:=LocalValue;
    LocalValue:=NewValue;
    Synchronizer.EndWrite;

    CriticalSectionChange.Enter;
// operator <> does not work with generics!
//    LocalChanged:=LocalChanged OR (OldValue<>NewValue);
    LocalChanged:=True;
    CriticalSectionChange.Leave;
  END;


 FUNCTION  TThreadSafeValue<T>.ReadDefaultValue;
  VAR Dummy:T;
  BEGIN
    // Read MyValue safely into dummy
    Synchronizer.BeginRead;
    Dummy:=LocalDefault;
    Synchronizer.EndRead;
    // and return as result
    Result:=Dummy;
  END;


 PROCEDURE TThreadSafeValue<T>.WriteDefaultValue(NewDefault:T);
  VAR OldValue:T;
  BEGIN
    Synchronizer.BeginWrite;
    LocalDefault:=NewDefault;
    OldValue:=LocalValue;
    LocalValue:=NewDefault;
    Synchronizer.EndWrite;

    CriticalSectionChange.Enter;
// operator <> does not work with generics!
//    LocalChanged:=LocalChanged OR (OldValue<>NewDefault);
    LocalChanged:=True;
    CriticalSectionChange.Leave;
  END;


 PROCEDURE TThreadSafeValue<T>.SetDefault;
  VAR MyDefaultValue:T;
  BEGIN
    Synchronizer.BeginRead;
    MyDefaultValue:=LocalDefault;
    Synchronizer.EndRead;

    Synchronizer.BeginWrite;
    LocalValue:=MyDefaultValue;
    Synchronizer.EndWrite;
  END;


 FUNCTION TThreadSafeValue<T>.GetChanged:Boolean;
  VAR MyChanged:Boolean;
  BEGIN
    CriticalSectionChange.Enter;
    MyChanged:=LocalChanged;
    LocalChanged:=False;
    CriticalSectionChange.Leave;

    Result:=MyChanged;
  END;     


I assume that this is a very common problem and - for my understanding - a threadvar does not help at all here.

How are others dealing with this?
« Last Edit: January 13, 2025, 11:26:19 am by YetAnotherDelphiLeaver »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5862
  • Compiler Developer
Re: Generics as PUBLISHED impossible?
« Reply #9 on: January 13, 2025, 09:04:43 pm »
After almost one year of trying different options I haven't still found a solution for using FPC generics as described (except, of course, the obvious solution to copy&paste the code snippet about 10 times for all possible variable types that I need, which is not really nice and in principle not good for code maintenance).

Did you read this?

@YetAnotherDelphiLeaver: please report a bug.

 

TinyPortal © 2005-2018