11

In Delphi XE2, I'm trying to overload the in operator on a record to allow me to check whether the value represented by the record is part of a set. My code looks like this:

type
  MyEnum = (value1, value2, value3);
  MySet = set of MyEnum;
  MyRecord = record
    Value: MyEnum;
    class operator In(const A: MyRecord; B: MySet): Boolean;
  end;

class operator MyRecord.In(const A: MyRecord; B: MySet): Boolean;
begin
  Result := A.Value in B;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MySet;
begin
  R.Value := value1;
  S := [value1, value2];
  Button1.Caption := BoolToStr(R in S);
end;

The code fails to compile. For the statement R in S the compiler says: Incompatible types MyRecord and MyEnum.

How can I overload the In operator on MyRecord so that R in S will evaluate to True in the above code?

Jan Goyvaerts
  • 21,379
  • 7
  • 60
  • 72
  • 1
    I don't think that what you are trying to achieve is possible... you should have better luck writing a the extra characters ".Value" => BoolToStr(R.Value in S ); and be done with it –  Nov 08 '11 at 06:05
  • The code in my question is just a simplified sample. In my actual application, the record type doesn't have a one-on-one correspondence with the set type. The workaround I ended up using was to add a `function InSet(S: MySet): Boolean` to the record and use that instead of the `in` operator. – Jan Goyvaerts Nov 30 '11 at 23:49
  • maybe good enough would be making member function instead - `BoolToStr(R._in(S));` – Arioch 'The Mar 22 '16 at 16:45

2 Answers2

5

For the in operator to work the right operand must be of the record type since it's a set operator and not a binary operator. In your case it is the left operand.

So the following will work:

type
  MyRecord = record
    Value: MyEnum;
    class operator In(const A: MyRecord; const B: MySet): Boolean;
  end;

  MyRecord2 = record
    Value: MySet;
    class operator In(const A: MyRecord; const B: MyRecord2): Boolean;
    class operator In(const A: MyEnum; const B: MyRecord2): Boolean;
  end;

class operator MyRecord.In(const A: MyRecord; const B: MySet): Boolean;
begin
  Result := A.Value in B;
end;

class operator MyRecord2.In(const A: MyRecord; const B: MyRecord2): Boolean;
begin
  Result := A.Value in B.Value;
end;

class operator MyRecord2.In(const A: MyEnum; const B: MyRecord2): Boolean;
begin
  Result := A in B.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  R2: MyRecord2;
begin
  R.Value := value1;
  R2.Value := [value1, value2];

  if R in R2 then;
  if value1 in R2 then;
end;
Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
1

Well, you can almost do this, but you may not want to. AFAIK, class operators only work on the class (or record) they are defined within, so both R and S in your code have to be TMyRecord. With some injudicious use of implicit casting, we get the following:

unit Unit2;
interface
type
  MyEnum = (value1, value2, value3);
  MySet = set of MyEnum;
  MyRecord = record
    Value: MyEnum;
    ValueSet: MySet;
    class operator Implicit(A: MyEnum): MyRecord;
    class operator Implicit(A: MySet): MyRecord;
    class operator In (Left,Right:MyRecord): Boolean;
  end;

implementation

class operator MyRecord.Implicit(A: MyEnum): MyRecord;
begin
  Result.Value := A;
end;

class operator MyRecord.Implicit(A: MySet): MyRecord;
begin
  Result.ValueSet := A;
end;

class operator MyRecord.In(Left, Right: MyRecord): Boolean;
begin
  Result:= left.Value in Right.ValueSet;
end;
end.

The following will now complile, and even work:

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MyRecord;
begin
  R.Value := value1;
  S := [value1,value2,value3];
  Button1.Caption := BoolToStr(R In S,true);
end;

Which, I'm sure we will all agree, is much more elegant than 'BoolToStr(R.Value in S)'. However the following will also compile, but give the wrong result:

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MyRecord;
begin
  R.Value := value1;
  S := [value1,value2,value3];
  Button1.Caption := BoolToStr(S In R,true);
end;

So, as Dorin commented, better to just have dull, staid old 'BoolToStr(R.Value in S)'. Unless of course you are being paid per line of code. And a bonus for bug-fixing.

HMcG
  • 2,062
  • 4
  • 24
  • 36
  • This particular question was just an exercise in figuring out class operators, which I had never used before. The record type in the actual code I was working on is a lot more complicated. The operators it defines allow the code that uses that record to be much simpler. A few hundred lines of simple operator functions allow thousands of lines of complex code to be much more readable. – Jan Goyvaerts Nov 30 '11 at 23:59
  • Sorry, my comments were somewhat facetious. I admit I am very taken with the new operator loading for records, and am using it widely. And I agree that it makes code much clearer in many cases. I was only poking fun at my own solution, as it is overkill in this particulary case ;-) – HMcG Dec 01 '11 at 22:08
  • "so both R and S in your code have to be TMyRecord" this is incorrect. – Johan Jan 29 '14 at 15:32