Jump to content

Tommi Prami

Members
  • Content Count

    602
  • Joined

  • Last visited

  • Days Won

    7

Posts posted by Tommi Prami


  1. On 4/12/2024 at 5:53 PM, Stefan Glienke said:

    TValue has no implicit casting rule for integer -> enum - thus it raises the invalid cast exception because you are passing an Integer to SetValue which gets implicit put into a TValue which gets transported further. It later does a type/cast check against the actual type of the property and fails.

    See TValue.Cast or TValue.TryCast and the Conversions matrix for more details.

    Have to try to remember that. Not used it too much.


    Thanks for helping!

     

    -Tee-


  2. 30 minutes ago, Lars Fosdal said:

    There also is https://docwiki.embarcadero.com/Libraries/Sydney/en/System.TypInfo.GetEnumProp

    which returns a PPropInfo, which again contains a PropType: PPTypeInfo, which you then can use with GetEnumValue?

    This lead me one step closer.

     

      var LEnumValue: Integer;
      LEnumValue := GetEnumValue(LProperty.PropertyType.Handle, AValue);
    
      if (LEnumValue <> -1) and LProperty.IsWritable then
        LProperty.SetValue(FInstance, LEnumValue);

    With this I get the actual value, but setting the value does not work (More than  less same code is used elsewhere and they work).

     

    It raises  EInvalidCast with message 'Invalid class typecast'.

    Integer is correct value for the Enum, it is in  between bounds. This slightly baffles me now.
     


  3. 7 minutes ago, msohn said:

    I've been using TypeInfo.SetEnumProp for that - works for all Enums that are published properties. It basically is SetOrdProp with GetEnumValue shown by Dalija, so saves you figuring out the enum size. 

    Oh, I forgot to mention that should work public properties also, I modify original, I still can... MAybe it is now more clear what I am after...

     

    -Tee-


  4. 10 minutes ago, Dalija Prasnikar said:

    I would use TypeInfo instead of RTTI as it will be faster. This requires System.TypInfo 

     

    
    procedure TMyThingy.SetEnumPropertyValue(const AValue: string);
    begin
      PByte(@FEnumProp)^ := GetEnumValue(TypeInfo(TMyEnum), AValue);
    end;

     

    You should pay attention on enum type size and use appropriate sized pointer when casting PByte(@EnumProp)^. This will also raise out of range exception if passed string does not match to any value in TMyEnum. You can catch that and set it to some default value if needed.


    I think I explained myself poorly. That won't work because it depends on TypeInfo(TMyEnum)

    My code must not know/depend on the (hardcoded) type, it should be dynamic.  Work with any given property, that is any type of Enum (some limitations may apply, but if works simple enums like one in example, it is OK).


  5. Yellow,

     

    I have about following situation.

    FInstance is any TObject descendant and enum property can be any public or published Enum property, code must not link to hard coded enum type.
     

    procedure TMyThingy.SetEnumPropertyValue(const AValue: string);
    var
      LContext: TRttiContext;
      LRtttiType: TRttiType;
      LProperty: TRttiProperty;
    begin
      LContext := TRttiContext.Create;
      LRtttiType := LContext.GetType(FInstance.ClassType);
      LProperty := LRtttiType.GetProperty(FPropertyName);
      
      // Here I should convert lets say TMyEnum = (A, B, C) from string into the property value
      // if I call SetEnumPropertyValue('B') property FPropertyName from FInstance-object should be set.
    end;

    This should be quite simple, couple lines of code most likely.

     

    Could not find sample code that was good enough fit to get this to work, there usually was too much knowns, like enum type TMyEnum,

     

    This should be totally dynamic.

    Circled around this quite long time, just could not find the way to connect all the dots...

     

    -Tee-
     


  6. 13 hours ago, Lars Fosdal said:

    @Tommi Prami Yes, the installer barfs on restarting the installation due to Navigator, but it should be able to recover. Mine did.

     

    Mine did not.

    Restarting encountered into same dll-error, don't remember which dll it was. and did not take screnn shot etc. 😞


    Coworker cant open IDE even after clean install... Freezes at the Splash Screen...
     

    -Tee-


  7. On 3/29/2024 at 9:00 PM, kuzduk said:

    i already wrote new site address with ssl setificate without antivirus damage!!!!!!!!!!!!!

     

     

    not use old site adress without ssl with h1n : https://kuzduk.h1n.ru

     

    Moderators, pleace edit first post link to  https://kuzduk.ru/delphi/kulibrary

     

    Could you instead of the site update all to the GitHUB, so it would be much easier and better place to public open source library. And keep it up there, and if needed link your own site to the GirHUB and maintain only that actyively.

     

    -Tee-


  8. 44 minutes ago, Lars Fosdal said:

     

    
    procedure ConnectFields(Query: TDataSet; const Fields: TArray<TField>; const Names: TArray<string>);
    begin
      Assert(Length(Fields) = Length(Names), 'Number of fields and number of names must match');
      for var ix := 0 to Length(Fields) - 1
      do begin
        Fields[ix] := Query.FieldByName(Names[ix]);
        if not Assigned(Fields[ix])
        then raise Exception.Create(Format('Field %s not found.', [Names[ix]]); 
      end;
    end
      
    // usage
    var
      Field1, Field2, Field3, Field4: TField;
    begin
      ConnectFields(Query,
       [ Field1,   Field2,   Field3,   Field4], 
       ['Field1', 'Field2', 'Field3', 'Field4']);
    ...

     which doesn't save you that much code, really.

    Does not work, know because I've tried that approach in the past, and retried your code and it does nothing. (outside of procedure, which is the important part),

     

    // usage
    var
      Field1, Field2, Field3, Field4: TField;
    begin
      Field1 := nil; // ...   Field2,   Field3,   Field4
      ConnectFields(Query,
       [ Field1,   Field2,   Field3,   Field4], 
       ['Field1', 'Field2', 'Field3', 'Field4']);
     // Fields star as they are in here, nil or not nil, doesnt matter... But eassy to check by nilling the fields becore and after call.

     

    Other problem in this tat compiler starts to complain about Fields are uninitialized. But that is separate issue...


  9. 15 minutes ago, uligerhardt said:

    Seems like an array should be the way:

    
    type
      TMyFields = array[1..4] of TField;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      LFields: TMyFields;
      i: Integer;
    begin
      for i := Low(TMyFields) to High(TMyFields) do
        LFields[i] := Query.FieldByName('FIELD' + IntToStr(i));
    end;

     

    Nice could be done something like that,. but field names are not indexed, usually and after that it is just the same as the my original example I am trying to solve...

     

    But Not looking for workaround...
     

    -Tee-


  10. 13 minutes ago, Sinspin said:

    You have also access to the list of fields. 

    
    Query.Fields[0..N]

    No need to address the fields always by their names.

    Yes...

    But totally different thing...

    And would have exact same problem. Would need to initialize variables for indexes by name. Constant magic number indexes leads to buggy code.

    I am looking for solution for getting fields, and also solution for other cases I could adapt it to.

    Getting fields is just one use case of this type of procedure I would like to use...

     

    -Tee-


  11. 1 hour ago, Lars Fosdal said:

    So it is not about visual use?

    If not, I need to understand more about how the TFields will be used.

    var
      LTotal: Double;
      LField1: TField;
      LField2: TField;
      LField3: TField;  
      LField4: TField;  
    begin
      LField1 := Query.FieldByName('FIELD1');
      LField2 := Query.FieldByName('FIELD1');
      LField3 := Query.FieldByName('FIELD1');
      LField4 := Query.FieldByName('FIELD1'); 
    
      Query.First;
      while not Query.Eof do
      begin
        LTotal := LTotal + LField1.AsFloat;
        // More stuff done with local field variables...
        Query.Next;
      end;

    In this case, TDataSet and TField are RTL stuff or their descendants...

    So implementation should be kind that works if I have no control over the API of the class..

     

    -Tee-


  12. 8 minutes ago, Lars Fosdal said:

    There are many ways to do this. Will you need to reference the TField components in any way, or are they just for display or editing? Will there be a need for different data format handing for different fields? Different titles, sizes, validation, etc?

    In this case I get the field usually when they are needed in the loop, or get rid of with-clause or something like that.


  13. Hello,

    I was pondering folowin situation, where there are easily quite many repeating code lines, and how to make int onew liner.

    var

    var
      LField1: TField;
      LField2: TField;
      LField3: TField;  
      LField4: TField;  
    begin
      LField1 := Query.FieldByName('FIELD1');
      LField2 := Query.FieldByName('FIELD1');
      LField3 := Query.FieldByName('FIELD1');
      LField4 := Query.FieldByName('FIELD1');  
      // ...
      // Should be nice to have it like this
      InitField(Query, [LField1, LField2, LField3, LField4], ['FIELD1', 'FIELD2', 'FIELD3', 'FIELD4', ]);

    It would be easy to make local procedure, case by case with var parameters etc, but that would not be very elegant.

    What I mean by dynamic, is that it would handle any number of fields and field names, oas long as there are same number of them passed to the procedure..

    Other case would be that could pass local variables and the routine would create those objects for me on single line.


  14. 8 hours ago, Vincent Parrett said:

    Embarcadero would need to move to a multi pass compiler to achieve this - something they have always resisted. I can understand this 10-15 yrs ago, but on modern hardware the compiler performance loss from multiple passes would not be that noticeable.

    Even something like 1.5 pass compiler that would enable some functionality or extend existing would be cool to have...

    -Tee-


  15. On 2/19/2024 at 2:30 PM, Lars Fosdal said:

    2. Make HighDPI actually works as intended - it is useless in a team as is - unless everyone runs the same scaling

    This would be good start.

    People in any teams have different level of eyesight. Different scaling needs etc. 

    This should have been implemented from the start the way that values in dfm would have been normalized to 96 as default dpi. Or something like that. Sure there could have been rounding errors, most likely. 

×