开发者

Implicit cast for overloaded record in Delphi as a parameter in a const array

We got rid of shortstring as part of a conversion from Delphi 7. I wanted to make it as painless as possible so we figured we could change the ShortString to some record which acted in the same way. Here's how it's declared (there's more to it, but this is the basic structure, which outlines the problem):

TShortStringRec = record 
private
  FStuff: array [0..49] of Char;
public
  class operator Implicit(AStuff: TShortStringRec): String;
  class operator Implicit(S1: String): TS开发者_如何转开发hortStringRec;
end;

This works well for setting strings to the record. But then there's functions like format which take as its parameter const array of const's. Is there any way to do an implict cast to what we'd want to pass into a const array?

function FunkyFunc : string;
var
  ssr : TShortStringRec;
begin
  ssr := 'Wall'; 
  result := format('Hello %s', [ssr]);  //<---error here
end;

Gives a syntax error while compiling because ssr is not a type of parameter that you can use on one of those arrays.


Short answer: No.
Long answer: What you're asking for is that the compiler to somehow know that you want a inherently untyped parameter to be coerced into the type you intend. The compiler simply doesn't have enough information at the call-site to make the determination. If you add an "Explicit" operator and then explicitly cast the parameter to a string, then it will work.


You could add the following to the public declaration :

function AsAnsiString : AnsiString;
function AsShortString : ShortString;

Then explicitly use the cast you want to use :

result := Format('hello %s',[ssr.AsAnsiString]);


I did something very similar in our migration from Delphi 2007 and also discovered that you can't use Format() to pass records to, and after reading the comments it makes perfect sense. An explicit cast (ideally to string) will tell the compiler what to do; the "explicit" methods are not required, however. As for suggestions to use "AsAnsiString": I personally don't like this idea because 1) extra function to write whereas an explicit cast can do the job 2) if readability is important, then so should be consistency, i.e. you do TShortStringRec.AsAnsiString, but do you also need to add an explicit method to set data, like SetAsAnsiString (or just do AsAnsiString as a property)? To me this defeats the point of implicit class operators. I recommend to stick to explicit casts, let the compiler determine which call is correct.

We use a lot of string[] types, so I auto-generated all my records. I thought it would be better to specify a default property to get AnsiChars out of ShortString types rather than have them get converted to UnicodeString then getting the char through [ ], for example:

type
 _ShortString3 = string[3]:
  ShortString3 = record
  private
    FData: _ShortString3;
    function GetAnsiChar(Index: Integer): AnsiChar;
    procedure PutAnsiChar(Index: Integer; const Value: AnsiChar);
  public
    class operator Implicit(const A: string): ShortString3;
    class operator Implicit(const A: ShortString3): string;
    class operator Equal(const A: ShortString3; B: AnsiChar): Boolean;
    class operator NotEqual(const A: ShortString3; B: AnsiChar): Boolean;
    class operator Equal(const A: ShortString3; B: ShortString3): Boolean;
    class operator NotEqual(const A: ShortString3; B: ShortString3): Boolean;
    class operator Add(const A: ShortString3; B: ShortString3): string;
    class operator Add(const A: ShortString3; B: AnsiChar): string;
    class operator Add(const A: ShortString3; B: string): string;
    property AnsiChars[Index: Integer]: AnsiChar read GetAnsiChar write PutAnsiChar; default;
end;    

FWIW here's the implementation:

{ ShortString3 }

function ShortString3.GetAnsiChar(Index: Integer): AnsiChar;
begin
  Result := FData[Index];
end;

procedure ShortString3.PutAnsiChar(Index: Integer; const Value: AnsiChar);
begin
  FData[Index] := Value;
end;

class operator ShortString3.Implicit(const A: string): ShortString3;
begin
  Result.FData := _ShortString3(A);
end;

class operator ShortString3.Implicit(const A: ShortString3): string;
begin
  Result := string(A.FData);
end;

class operator ShortString3.Equal(const A: ShortString3; B: AnsiChar): Boolean;
begin
  Result := A.FData = B;
end;

class operator ShortString3.NotEqual(const A: ShortString3; B: AnsiChar): Boolean;
begin
  Result := A.FData <> B;
end;

class operator ShortString3.Equal(const A: ShortString3; B: ShortString3): Boolean;
begin
  Result := A.FData = B.FData;
end;

class operator ShortString3.NotEqual(const A: ShortString3; B: ShortString3): Boolean;
begin
  Result := A.FData <> B.FData;
end;

class operator ShortString3.Add(const A: ShortString3; B: ShortString3): string;
begin
  Result := string(A.FData + B.FData);
end;

class operator ShortString3.Add(const A: ShortString3; B: AnsiChar): string;
begin
  Result := string(A.FData + B);
end;

class operator ShortString3.Add(const A: ShortString3; B: string): string;
begin
  Result := string(A.FData) + B;
end;

This has been turned out to be overall a good trick because we didn't manually fiddle with hundreds of files, instead, just wrote 1 file with all our custom ShortString records with implicit class operators. (There was an intermediate step that automatically changed all ShortString types to our own and added the unit StringTypes to the uses, but it was safe.) Thousands of ShortString related warnings disappeared.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜