Recent

Author Topic: Question regarding Generics.Collections  (Read 300 times)

JdeHaan

  • Jr. Member
  • **
  • Posts: 51
Question regarding Generics.Collections
« on: May 23, 2020, 06:07:34 pm »
Hi,

I have a variant record like this:

Code: Pascal  [Select][+][-]
  1. uses Generics.Collections;
  2.  
  3. type
  4.   TValue = record
  5.     class operator =(a, b: TValue):Boolean;
  6.     class operator >(a, b: TValue):Boolean;
  7.     case Typ: TValueType of
  8.       vtBool:   (Bool: Boolean);
  9.       vtNumber: (Number: Double);
  10.       vtInt:    (Int: Int64);
  11.       vtString: (Chars: PChar; LengthStr: LongInt);
  12.       vtChar:   (Char: PChar; LengthChar: LongInt);
  13.       vtByte:   (Byte: PByte; LengthByte: LongInt);
  14.       vtObj:    (Obj: TObject);
  15.   end;
  16.  
  17. implementation
  18.  
  19. class operator TValue. = (a, b: TValue): Boolean;
  20. begin
  21.   Result := a.Equals(b);
  22. end;
  23.  
  24. class operator TValue.>(a, b: TValue): Boolean;
  25. begin
  26.   Result := a.Greater(b);
  27. end;
  28.  
  29. function TValueHelper.Equals(Other: TValue): Boolean;
  30. begin
  31.   if Self.Typ <> Other.Typ then
  32.     Result := False
  33.   else
  34.     case Typ of
  35.       vtBool:   Result := Bool = Other.Bool;
  36.       vtNil:    Result := True;
  37.       vtNumber: Result := Number = Other.Number;
  38.       vtInt:    Result := Int = Other.Int;
  39.       vtString: Result := StrLComp(Chars, Other.Chars, LengthStr) = 0;
  40.       vtChar:   Result := StrLComp(Self.Char, Other.Char, Self.LengthChar) = 0;
  41.       vtByte:   Result := CompareByte(Self.Byte, Other.Byte, Self.LengthByte) = 0;
  42.       vtObj:    Result := Obj = Other.Obj;
  43.     end;
  44. end;
  45.  
  46. function TValueHelper.Greater(Other: TValue): Boolean;
  47. begin
  48.   if Self.Typ <> Other.Typ then
  49.     Result := False
  50.   else
  51.     case Typ of
  52.       vtBool:   Result := False;
  53.       vtNil:    Result := False;
  54.       vtNumber: Result := Number > Other.Number;
  55.       vtInt:    Result := Int > Other.Int;
  56.       vtString: Result := StrLComp(Chars, Other.Chars, LengthStr) > 0;
  57.       vtChar:   Result := StrLComp(Self.Char, Other.Char, Self.LengthChar) > 0;
  58.       vtByte:   Result := CompareByte(Self.Byte, Other.Byte, Self.LengthByte) > 0;
  59.       vtObj:    Result := False;
  60.     end;
  61. end;
  62.  


Then, I have an array defined as:

TElements = specialize TList<TValue>;  // from Generics.Collections

I can add items to the list, I can delete items at an index, retrieve an item at a certain index, BUT, I can't get indexOf(value) or contains(value) to produce anything else but -1 and false.

What am I missing here?  :(

---------------------------

MacOs Cataline; Laz 2.1; FPC 3.3.1

bytebites

  • Sr. Member
  • ****
  • Posts: 276
Re: Question regarding Generics.Collections
« Reply #1 on: May 23, 2020, 07:08:44 pm »
This code snippet gives correct result
Quote
1
TRUE
-1
FALSE

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}
  3. {$ModeSwitch advancedrecords}
  4. uses Generics.Collections, sysutils;
  5.  
  6. type
  7.   TValueType =(vtBool,
  8.        vtNil,
  9.        vtNumber,
  10.        vtInt,
  11.        vtString,
  12.        vtChar,
  13.        vtByte,
  14.        vtObj);
  15.   TValue = record
  16.     class operator =(a, b: TValue):Boolean;
  17.     class operator >(a, b: TValue):Boolean;
  18.     case Typ: TValueType of
  19.       vtBool:   (Bool: Boolean);
  20.       vtNumber: (Number: Double);
  21.       vtInt:    (Int: Int64);
  22.       vtString: (Chars: PChar; LengthStr: LongInt);
  23.       vtChar:   (Char: PChar; LengthChar: LongInt);
  24.       vtByte:   (Byte: PByte; LengthByte: LongInt);
  25.       vtObj:    (Obj: TObject);
  26.   end;
  27.  
  28.  { TValueHelper }
  29.  
  30.  TValueHelper= record helper for tvalue
  31.  
  32.  private
  33.    function Equals(Other: TValue): Boolean;
  34.    function Greater(Other: TValue): Boolean;
  35.  end;
  36.  
  37.  TElements = specialize TList<TValue>;
  38. var
  39.   el:specialize TList<tvalue>;
  40.   v: tvalue;
  41.  
  42. class operator TValue. = (a, b: TValue): Boolean;
  43. begin
  44.   Result := a.Equals(b);
  45. end;
  46.  
  47. class operator TValue.>(a, b: TValue): Boolean;
  48. begin
  49.   Result := a.Greater(b);
  50. end;
  51.  
  52. function TValueHelper.Equals(Other: TValue): Boolean;
  53. begin
  54.   if Self.Typ <> Other.Typ then
  55.     Result := False
  56.   else
  57.     case Typ of
  58.       vtBool:   Result := Bool = Other.Bool;
  59.       vtNil:    Result := True;
  60.       vtNumber: Result := Number = Other.Number;
  61.       vtInt:    Result := Int = Other.Int;
  62.       vtString: Result := StrLComp(Chars, Other.Chars, LengthStr) = 0;
  63.       vtChar:   Result := StrLComp(Self.Char, Other.Char, Self.LengthChar) = 0;
  64.       vtByte:   Result := CompareByte(Self.Byte, Other.Byte, Self.LengthByte) = 0;
  65.       vtObj:    Result := Obj = Other.Obj;
  66.     end;
  67. end;
  68.  
  69. function TValueHelper.Greater(Other: TValue): Boolean;
  70. begin
  71.   if Self.Typ <> Other.Typ then
  72.     Result := False
  73.   else
  74.     case Typ of
  75.       vtBool:   Result := False;
  76.       vtNil:    Result := False;
  77.       vtNumber: Result := Number > Other.Number;
  78.       vtInt:    Result := Int > Other.Int;
  79.       vtString: Result := StrLComp(Chars, Other.Chars, LengthStr) > 0;
  80.       vtChar:   Result := StrLComp(Self.Char, Other.Char, Self.LengthChar) > 0;
  81.       vtByte:   Result := CompareByte(Self.Byte, Other.Byte, Self.LengthByte) > 0;
  82.       vtObj:    Result := False;
  83.     end;
  84. end;
  85.  
  86. begin
  87.   el:=TElements.create;
  88.   v.Int:=4;
  89.   v.Typ:=vtInt;
  90.   el.Add(v);
  91.   v.Bool:=true;
  92.   v.Typ:=vtBool;
  93.   el.Add(v);
  94.   writeln(el.IndexOf(v));
  95.   writeln(el.Contains(v));
  96.   v.typ:=vtNil;
  97.   writeln(el.IndexOf(v));
  98.   writeln(el.Contains(v));
  99. end.
  100.  

JdeHaan

  • Jr. Member
  • **
  • Posts: 51
Re: Question regarding Generics.Collections
« Reply #2 on: May 23, 2020, 07:25:09 pm »
Thanks bytebites,
Indeed your code works. So, my problem lies somewhere else, since this is more or less how I use it as well.

PascalDragon

  • Hero Member
  • *****
  • Posts: 1528
  • Compiler Developer
Re: Question regarding Generics.Collections
« Reply #3 on: May 23, 2020, 09:15:39 pm »
The types in Generics.Collections use structural equivalence for records by default. If you want something else you'll need to implement a IComparer<TValue> interface and pass that in the constructor of TElements (you can for example use Generics.Defaults.TComparer<TValue> which allows you to pass a function or method pointer.

Sidenote: for your strings (vtString and vtChar) you should check the string length in addition to the content as otherwise e.g. "Hello" and "Hello World" will be considered the same due to StrLComp getting the length of the first string. Same also applies to vtByte.

JdeHaan

  • Jr. Member
  • **
  • Posts: 51
Re: Question regarding Generics.Collections
« Reply #4 on: May 23, 2020, 10:44:54 pm »
Thanks PascalDragon,
Indeed Bytebites code works without the operator overloads.

Also, thanks for the hint about checking the length of the strings... completely oversaw that  %)

PascalDragon

  • Hero Member
  • *****
  • Posts: 1528
  • Compiler Developer
Re: Question regarding Generics.Collections
« Reply #5 on: May 23, 2020, 10:49:21 pm »
Indeed Bytebites code works without the operator overloads.

It will fail if you use for example different PChars that have the same content (and thus should be equal according to your operator overload).

 

TinyPortal © 2005-2018