### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Question regarding Generics.Collections  (Read 302 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}
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;
91.   v.Bool:=true;
92.   v.Typ:=vtBool;
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).