5
votes

i have a problem about conversion of type using overload of operator, here i have an full example code:

program OVerloads;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TMyRange = record
  private type
    TMyRangeEnum = 1 .. 90;
  var
    FMyRangeEnum: TMyRangeEnum;
  public
    class operator Implicit(const Value: Integer): TMyRange;
    class operator Implicit(const Value: TMyRange): Integer;
    class operator Explicit(const Value: Integer): TMyRange;
    class operator Explicit(const Value: TMyRange): Integer;
  end;

class operator TMyRange.Implicit(const Value: Integer): TMyRange;
begin
    Result.FMyRangeEnum := Value;
end;

class operator TMyRange.Implicit(const Value: TMyRange): Integer;
begin
  Result := Value.FMyRangeEnum;
end;

class operator TMyRange.Explicit(const Value: Integer): TMyRange;
begin
  Result.FMyRangeEnum := Value;
end;

class operator TMyRange.Explicit(const Value: TMyRange): Integer;
begin
  Result := Value.FMyRangeEnum;
end;

var
 MyValue: TMyRange;
 MyVar: TMyRange; // (or Integer, not change nothing)
 MyArr: array[1..90] of TMyRange; // (or Integer, not change nothing) 
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }

    MyValue := 90;
    Writeln(MyValue);  // [1] Not work, error: E2054 Illegal in Write/Writeln statement

    Writeln(Integer(MyValue));  // Work

    MyVar := MyArr[MyValue];   // Not work, error: E2010 Incompatible types: 'Integer' and 'TMyRange'                

    MyVar := MyArr[Integer(MyValue)];  // work      

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Probably i have forgotten something, but as i can solve problem [1] in mnode that not need explicit all time Integer(myValue) ? Thanks again, very much.

3
ok thanks :) in this case i have used the explicit conversion too Maybe me not understood well? In general i understood becouse it, but wanted to try to understand better if can solve writing: writeln(MyValue) to place of: writeln(integer(MyValue) or: MyVar := MyArr[MyValue] to place of: MyVar := MyArr[integer(MyValue)]; in general it not big problem of course; but in a procedure/function look much Integer(MyValue) i don't know, i have impression that not correct, that there is a better solution. Just it. - Marcello Impastato

3 Answers

3
votes

You appear to be overcomplicating this. Define a range type like this:

type
  TMyRange = 1..90;

Then declare your array like this:

var
  MyArr: array[TMyRange] of TMyRange;

And that's it.


For whatever reason, you assert that you need to use a record here with operator overloading. Be that as it may, but you have pretty much identified that operator overloading implicit casts can not be used to shoehorn your type to be compatible with Writeln and integer array indexing. I can't find any documentation that describes these scenarios, but it's pretty clear that the compiler won't let you do what you want.

So far as I can tell, you can rely on the implicit cast for actual parameters and the right hand side of assignment statements.

Here is my minimal sample to demonstrate the options:

program Overloads;
{$APPTYPE CONSOLE}
type
  TRec = record
  private
    function GetOrd: Integer;
  public
    class operator Implicit(const Value: TRec): Integer;
    property ord: Integer read GetOrd;
  end;

class operator TRec.Implicit(const Value: TRec): Integer;
begin
  Result := 0;
end;

function TRec.GetOrd: Integer;
begin
  Result := 0;
end;

procedure Foo(i: Integer);
begin
end;

var
  R: TRec;
  a: array[0..0] of Integer;

begin
  Writeln(R);//E2054 Illegal type in Write/Writeln statement
  Writeln(Integer(R));//explicit cast, provided by class operator Implicit
  Writeln(R.ord);//my preferred option, a property
  a[R] := 0;//E2010 Incompatible types: 'Integer' and 'TRec'
  a[Integer(R)] := 0;//again, explicit cast is fine
  a[R.ord] := 0;//or using a property
  Foo(R);//implicit cast used for actual parameters
end.

Notes:

  1. If you are supplying an implicit cast operator then there's no need to supply an explicit cast operator also. An explicit cast will result in the implicit operator being called.
  2. I think a property on the record type results in more readable code than an explicit cast.
0
votes

I don't believe implicit operators work in Write/WriteLn, as they are compiler-magic functions and not "proper" functions that take a specific type.

0
votes

The only way I see is overloading the Writeln-function:

procedure Writeln(wrt: Variant);
begin
  System.Writeln(wrt);
end;

You either have to change your exception handler to use System.Writeln or concat the strings, because open parameter count is not supported for Delphi functions.

Edit:
Like you mention in your comment you are getting an error when trying to use your record as array index.

This is because array indexes, aswell as some other constructs, like for example loop variables and case switches, require ordinal types (such as Integer, Byte, Word ...).
Passing a record won't work there, because a record is not an ordinal type. The possibility to cast it to an ordinal type doesn't make it one.

There is a simple solution:

Define TMyRangeEnum as a standalone type, without a record enclosing it.
This will not only solve the problems you experience but also ones you didn't experience yet:

MyValue := 91;

will result in a compiler error instead of setting the enum variable to an invalid value.

And so will:

MyValue := 256;

result in such an error, instead of an overflow (0 will be assigned instead, which is also not a valid value in the enum).