Recent

Author Topic: Trouble with generics  (Read 443 times)

imekon

  • New Member
  • *
  • Posts: 11
Trouble with generics
« on: August 17, 2019, 12:04:33 am »
I'm seeing a problem using generics, here's some sample code:

https://github.com/imekon/generics

If you run it you get:

Generic Properties
1 ''
2 ''
Press RETURN...

I'd expect to see

Generic Properties
1 'Name'
2 'Strength'
Press RETURN...

I can see with the debugger the _name variable has been corrupted.

Any ideas?

mr-highball

  • Jr. Member
  • **
  • Posts: 61
Re: Trouble with generics
« Reply #1 on: August 17, 2019, 06:29:27 am »
you need to use data instead of Items in Dump()
Code: Pascal  [Select]
  1. procedure TThing.Dump;
  2. var
  3.   i: integer;
  4.   prop: TProperty;
  5.  
  6. begin
  7.   for i := 0 to _properties.Count - 1 do
  8.   begin
  9.     prop := TProperty(_properties.Data[i]);
  10.     WriteLn(i + 1, ' ''', prop.Name, '''');
  11.   end;
  12. end;
  13.  

Also, you're using a standard map, rather than an object map, so the destructor won't free the properties. To fix, change your definition of properties to,

Code: Pascal  [Select]
  1. TProperties = specialize TFPGMapObject<string, TProperty>;
  2.  
-Highball

Akira1364

  • Hero Member
  • *****
  • Posts: 522
Re: Trouble with generics
« Reply #2 on: August 17, 2019, 06:35:53 am »
The typecast to TProperty is pointless, also, since it's a generic map... they can just write this, with no intermediate variable at all:

Code: Pascal  [Select]
  1. procedure TThing.Dump;
  2. var
  3.   i: integer;
  4. begin
  5.   for i := 0 to _properties.Count - 1 do
  6.     WriteLn(i + 1, ' ''', _properties.Data[i].Name, '''');
  7. end;

It's not really great that you can access the "items" default property (from the base class of non-generic TFPSList) through TFPGMap in the first place, I'll note as well.
« Last Edit: August 17, 2019, 06:46:58 am by Akira1364 »

mr-highball

  • Jr. Member
  • **
  • Posts: 61
Re: Trouble with generics
« Reply #3 on: August 17, 2019, 06:38:45 am »
The typecast to TProperty is pointless, also, since it's a generic map... they can just write this, with no intermediate variable at all:

Code: Pascal  [Select]
  1. procedure TThing.Dump;
  2. var
  3.   i: integer;
  4. begin
  5.   for i := 0 to _properties.Count - 1 do
  6.     WriteLn(i + 1, ' ''', _properties.Data[i].Name, '''');
  7. end;

Agreed, but sometimes when debugging, the local variable reference offers more insight (not sure if that was the intention).
I know sometimes the debugger refuses to evaluate sometimes without the ref.
-Highball

imekon

  • New Member
  • *
  • Posts: 11
Re: Trouble with generics
« Reply #4 on: August 17, 2019, 08:54:17 am »
Ah, that worked well! Thanks!

imekon

  • New Member
  • *
  • Posts: 11
Re: Trouble with generics
« Reply #5 on: August 17, 2019, 10:47:36 am »
I'd like to display the value of the property, so I changed the Dump function:

Code: Pascal  [Select]
  1. procedure TThing.Dump;
  2. var
  3.   i: integer;
  4.   value: string;
  5.  
  6.   prop: TProperty;
  7.  
  8. begin
  9.   for i := 0 to _properties.Count - 1 do
  10.   begin
  11.     prop := _properties.Data[i];
  12.  
  13.     if prop is TStringProperty then
  14.        value := (prop as TStringProperty).Value;
  15.  
  16.     if prop is TFloatProperty then
  17.        value := FloatToStr((prop as TFloatProperty).Value);
  18.  
  19.     WriteLn(i + 1, ' ', prop.Name, ' ', value);
  20.   end;
  21. end;
  22.  

Is there any way to eliminate the testing for the different types of value? I thought of adding a function to TProperty but how do I override it in generic TValueProperty?

Thaddy

  • Hero Member
  • *****
  • Posts: 8664
Re: Trouble with generics
« Reply #6 on: August 17, 2019, 01:40:46 pm »
A bit sneaky, but how about this?:
Code: Pascal  [Select]
  1. {
  2.     if prop is TStringProperty then
  3.        value := (prop as TStringProperty).Value;
  4.  
  5.     if prop is TFloatProperty then
  6.        value := FloatToStr((prop as TFloatProperty).Value);
  7. }
  8. // replace all that by just one line
  9.     writestr(value,prop.value);
As long as the type of value is supported by writestr that will be OK.

In your above code even a one liner for everything:
Code: Pascal  [Select]
  1.     WriteLn(i + 1, ' ', prop.Name, ' ', prop.value);
« Last Edit: August 17, 2019, 01:49:56 pm by Thaddy »
Most people that want to use threading should learn to patch their jeans first: use a needle.

imekon

  • New Member
  • *
  • Posts: 11
Re: Trouble with generics
« Reply #7 on: August 18, 2019, 09:52:37 am »

If prop is of type TProperty, then I get an error with:

Code: Pascal  [Select]
  1. WriteLn(i + 1, ' ', prop.Name, ' ', prop.Value);
  2.  
thing.pas(63,46) Error: identifier idents no member "Value"

because TProperty has no field called Value. Derived class TValueProperty does but it's a generic class and you have to specialise to use it. Hence the "if prop is TStringProperty..." what I'm looking I don't think can be done.

Thaddy

  • Hero Member
  • *****
  • Posts: 8664
Re: Trouble with generics
« Reply #8 on: August 18, 2019, 02:01:32 pm »

If prop is of type TProperty, then I get an error with:

Code: Pascal  [Select]
  1. WriteLn(i + 1, ' ', prop.Name, ' ', prop.Value);
  2.  
thing.pas(63,46) Error: identifier idents no member "Value"

because TProperty has no field called Value. Derived class TValueProperty does but it's a generic class and you have to specialise to use it. Hence the "if prop is TStringProperty..." what I'm looking I don't think can be done.
Well, so only value properties then:
Code: Pascal  [Select]
  1.  if prop is TValueProperty then WriteLn(i + 1, ' ', prop.Name, ' ', prop.Value);
Will that do?

Or may be:
Code: Pascal  [Select]
  1. function ValueProp(const p:Tproperty):string;
  2. begin
  3.   if p is TValueProperty then result := TValueProperty(p).Value else result :='';
  4. end;
  5. begin
  6.   WriteLn(i + 1, ' ', prop.Name, ' ', ValueProp(prop));
  7. end.

Not tested. Tested. Works!
But you may want to validate the typekind if it can be handled by writeln/writestr: if gettypekind(TValueproperty(prop).Value) in (tkAString,etc... inside the valueprop function.

Along these lines:
Code: Pascal  [Select]
  1. function ValueProp(const p:Tproperty):string;
  2. begin
  3. {$push}{$B-} // make sure of shortcut evaluation *
  4.   if (p is TValueProperty)
  5.   // probably not complete, check!
  6.   and (GetTypeKind(TValueProperty(p).Value) in [tkAstring,TkSString,tkWString,tkSet,tkChar,tkWChar,TkInteger,tkFloat])  then
  7.      result := TValueProperty(p).Value
  8.   else
  9.      result :='';
  10. {$pop}
  11. end;
*  Otherwise the and can cause an error!
« Last Edit: August 18, 2019, 03:33:43 pm by Thaddy »
Most people that want to use threading should learn to patch their jeans first: use a needle.