overload operator and conversion type

Tag: delphi , delphi-xe2 Author: simple_ting Date: 2012-02-16

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.

Best Answer

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.

comments:

+1 you gotta love the simple answers! (:
Yes, in this case work. But in mine case i need use overload operator. Of course, the code that i have reported here was just for reproduce the problem. So not there solution for it? need use necessary conversion with: Integer(variable)?
Ok thanks, i try to apply your suggest too. Thank very much :)

Other Answer1

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.

comments:

The problem not is only with write/writeln; If for example i write: MyVar := MyArr[Myvalue]; (where: MyVar is a integer variable; MyArr a array of integer and MyValue is a TMyRange type variable) i get in this case this error: "E2010 Incompatible types 'Integer' and 'TMyRange'". Some idea some solve?

Other Answer2

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).

comments:

Hello, just done as you told but nothing, The problem is about variable that need convert in integer type explicitely. For me not is a problem use: integer(variable) but wanted try if there was a solution more "clear/better" that so.