11

Take this small example class (not my real code, but it exposes the problem):

Convert = class(TObject)
  public
    class function ToString(value: Double): String; overload;
    class function ToString(value: TDateTime): String; overload;
  end;

It compiles fine until you try to use the Double or TDateTime functions As In:

var
  d: Double;
begin
  d := 99.99;
  ShowMessage(Convert.ToString(d));

You will get this compile error: Ambiguous overloaded call to 'ToString'. The problem boils down to the fact that TDateTime is a type of Double

My Question: how do You deal with this type of problem?

EDIT - I am NOT looking for a solution for the example given

I have found 3 Solutions so far:

  • Rename one of the 2 functions
  • Add a "Dummy" parameter to one of the 2 functions
  • Change the parameters to Var types, this has the disadvantage that I can no longer call this function with constants

are there any other solutions out there?

whosrdaddy
  • 11,720
  • 4
  • 50
  • 99
  • I rename those DateTime functions – Sir Rufo Jan 05 '13 at 08:22
  • If it gets ugly I'd rename them all: `DoubleToString`, `DateToString` and skip the `overload`. Using `var` parameters is a bad idea: makes the functions less useful and confusing. Using double-standards (some functions overloaded some functions not) is again confusing. – Cosmin Prund Jan 05 '13 at 08:26
  • I'm using the Dummy parameter at the moment. The real code uses a fluent interface and renaming the function just doesn't feel right. I am hoping there is a better solution out there... – whosrdaddy Jan 05 '13 at 08:33
  • 1
    @CosminPrund: Renaming them all is out of the question (in reality I have 30+ overloads) – whosrdaddy Jan 05 '13 at 08:39
  • dealing with aliases (TDateTime is an alias) always causes this kind of ambiguous behavior – Sir Rufo Jan 05 '13 at 08:41
  • @SirRufo: Yeah I know, but should Delphi not respect the fact that it is another Type? (even if it is an alias). I believe .NET doesn't have this problem... – whosrdaddy Jan 05 '13 at 08:49
  • maybe, but it doesn't - just pass a TDateTime to TValue and you will not know from TValue that it was a TDateTime. http://docwiki.embarcadero.com/Libraries/XE2/en/System.TypInfo.TTypeKind - In fact you get the same bytecode using Double, Extended or TDateTime. How can this resolved to the alias type? (just spoken Delphi) – Sir Rufo Jan 05 '13 at 08:54
  • @SirRufo Indeed but in RTTI you would look at the TRttiMember.MemberType's Name and you know what Type you have – whosrdaddy Jan 05 '13 at 09:02
  • I tested your example in XE,XE2,XE3 and they compile and execute fine. – LU RD Jan 05 '13 at 09:08
  • @LURD: doesn't matter that's just an example (it is not my real code). I just want some advice from You guys how you would deal with this kind of problem... – whosrdaddy Jan 05 '13 at 09:11
  • 1
    @whosrdaddy, then post real code that exposes your problem. – LU RD Jan 05 '13 at 09:14
  • 3
    @LURD `ShowMessage( Convert.ToString( 99.99 ) );` that causes the `E2251` compiler error – Sir Rufo Jan 05 '13 at 09:24
  • Add that to your question, but the simple solution here is: `Convert.ToString( Double(99.99))` to avoid ambiguity. – LU RD Jan 05 '13 at 09:28
  • @LURD I am asking this question in a broader sense, how would YOU deal with this problem, I am not asking for a solution for the code I posted. David's answer is a good example of what I want... – whosrdaddy Jan 05 '13 at 09:33
  • Renaming the functions is a good advice, but you already gave that option in your question. Opting for a an explicit type definition in case of constants as in my comment above is another alternative. – LU RD Jan 05 '13 at 09:36
  • @LURD `Convert.ToString( Double(99.99) )` -> `E2089` compiler error (Invalid Typecast) – Sir Rufo Jan 05 '13 at 09:45
  • @SirRufo indeed, my bad. – LU RD Jan 05 '13 at 09:46
  • @LURD no problem, I had the same in mind but a running IDE at sight to test before post, my luck :o) – Sir Rufo Jan 05 '13 at 09:49
  • 1
    @SirRufo, `Convert.ToString( Double(Variant(99.99)))` works. – LU RD Jan 05 '13 at 10:24
  • @LURD +1 a real double conversion :o) – Sir Rufo Jan 05 '13 at 13:23
  • @whosrdaddy I don't think .NET has this problem because .NET probably doesn't use a `double` as its `DateTime` class. – Jerry Dodge Jan 07 '13 at 16:37
  • @JerryDodge it is not about double/datetime, I just gave an example to illustrate the problem - look at the problem in a broader sense – whosrdaddy Jan 07 '13 at 18:09

3 Answers3

14

Overloaded methods can be very effective. However, as soon as there is a hint of ambiguity they become a liability. A good example of this are the new TStream overloads introduced in XE3. It's not hard to fall into a trap where the compiler chooses an overload that you weren't expecting. At least in your code the compiler stopped. In that sense you were lucky.

So my advice, in your situation, is to abandon overloads. Express the different input types in the method name. Yes it's a little more verbose, but you won't make any mistakes, and you code will compile!

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
5

Your posted example compiles and executes fine in XE.

In a comment you give this example instead:

ShowMessage( Convert.ToString( 99.99 ));  // <- gives compiler error 2251

In this particular case the solution is to explicitly define the type( I thought):

ShowMessage( Convert.ToString( Double(99.99) )); // <- E2089, Invalid Typecast

Looking into the documentation:

This error message is issued for type casts not allowed by the rules. The following kinds of casts are allowed:

  • Ordinal or pointer type to another ordinal or pointer type
  • A character, string, array of character or pchar to a string
  • An ordinal, real, string or variant to a variant
  • A variant to an ordinal, real, string or variant
  • A variable reference to any type of the same size.

So, to explicitly tell the compiler to select the Double overloaded function:

ShowMessage( Convert.ToString( Double(Variant(99.99)))); // Ok

A bit convoluted perhaps. But for the other overloaded function it is simpler:

ShowMessage( Convert.ToString( EncodeDate(2013,1,5));

Update


To make this a generic solution working for all classes, consider adding class functions to resolve your ambiguous types.

Convert = Class(TObject)
  ...
  class function AsDouble( value: Double) : Double; inline; static;
  class function AsTDateTime( value: TDateTime) : TDateTime; inline; static;
end;

class function Convert.AsDouble(value: Double): Double;
begin
  Result := Value;
end;
class function Convert.AsDateTime(value: TDateTime): TDateTime;
begin
  Result := Value;
end;

Now you can call your overloaded class function with constants:

ShowMessage( Convert.ToString( Convert.AsDouble(99.99)));   
LU RD
  • 34,438
  • 5
  • 88
  • 296
  • My answer is more about how to specify the intended type for an overloaded function call, in those cases the compiler is not able to resolve that without help. – LU RD Jan 05 '13 at 11:35
  • 1
    I see, typecasting could be a solution but I then I prefer renaming the function :) (+1 for your answer) – whosrdaddy Jan 05 '13 at 12:05
  • It seems a little awkward to convert something from integers, to a string, to a date, back to a string again... – Jerry Dodge Jan 07 '13 at 17:12
1

How about collapsing it all?:

class function Convert.ToString(value: Variant): String;
begin
   Result := VarToStr(Value);
end;
Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
  • The point is not about a String Conversion but how you would deal with ambiguous overload parameters. This solution has one big problem, it opens the parameter possibility to more types than intended – whosrdaddy Jan 05 '13 at 11:26
  • My point isn't about a string either ;-) but about the variant. You're right about the 'more types then intended' but if it's the OP's own code he can just support the intended types and uses an 'else' or an assertion for the rest. – Jan Doggen Jan 05 '13 at 19:27
  • I was actually going to suggest this and then I saw your answer, Rather than immediately calling `VarToStr()` you can instead check the type of the value and handle it differently accordingly. – Jerry Dodge Jan 07 '13 at 17:09
  • PS - Commenter is the OP, and also, perhaps you should demonstrate the type checking / handling in the code of your answer :) – Jerry Dodge Jan 07 '13 at 17:10