5

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): TShortStringRec;
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.

7
  • 1
    @David, I left out the rest which makes having a record worthwhile. Plus the part we don't know where we try to access indexes we formerly expected to have in the string. We can easily cast it to a string. If you know the answer is no, it's impossible, then by all means answer that way. Commented Sep 2, 2011 at 18:28
  • Can you give a specific code example of what you are trying to do with that record that isn't working? Commented Sep 2, 2011 at 18:36
  • I find it very hard to imagine that you can't solve the problem better with string Commented Sep 2, 2011 at 18:43
  • @David Heffernan, FStuff is really supposed to be two strings. Some times we'll want to use it as two strings sometimes we'll want to insert it into a database as one string. It's not my code, but I do agree with you, for the most part we just changed all our shortstrings to string to avoid deprication. Commented Sep 2, 2011 at 18:46
  • 1
    That is in fact the same problem: the compiler doesn't know you want to convert the rec via a string to a variant. You'll have to tell it. Commented Sep 2, 2011 at 21:22

3 Answers 3

7

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.

Sign up to request clarification or add additional context in comments.

10 Comments

OK, that's pretty much what we were doing, although the guy I showed this to was grumbling so I figured I'd ask here. What would be so wrong with having class operators not need result types, and have them implicitly be whatever-it-is-you-call-it-when-you-pass-into-a-const-array-of-const's?
@Peter - Because the value stored in array of const is a record, not a type like string, unless you explicitly cast it when passing it in the array of const.
Well, the information is there, but its use is not part of the language definition. The compiler knows the record type is not a known type that can be passed to an open array of const. The compiler could look into the record type to see if it has an implicit conversion to one of the permissible built-in types, and if there is one and only one match, perform that conversion.
@Peter - Because getting the compiler to allow you to pass your record to the open array param is only half the problem. The other half is what is the receiving function (format) supposed to do with your record structure? Format() won't know what to do with it, so there's no point in passing it in as a record. Conversion to a standard type should happen before it goes into the open array so that functions that take open array of const params only have to deal with a finite number of options.
@dthorpe - Then in version two, you innocently add another implicit conversion... not knowing that it is being used in this manner. What then?
|
2

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]);

3 Comments

Well, wouldn't it just be easier to use explicit cast?
Yes, but I think this cast gives more information of the intention. Which produces more readable code. Also added here as an alternative to using the explicit cast.
This isn't a cast, its a method. How does it give more information than AnsiString(ssr)?
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.