3

I have an object that sole responsability is to hold a value. This value can be passed in directly via constructor, or the constructor can take an anonymous function which tells the object how to get the value.

type
  TDefineCached<T> = Reference to function: T;

  TValue<T> = class(TInterfacedObject, IValue<T>)
  private
    FValue: T;
    FActive: Boolean;
    FDefine: TDefineCached<T>;
    procedure DoDefine;
  public
    constructor Create(const Define: TDefineCached<T>);
    class function New(const Define: TDefineCached<T>): IValue<T>; overload;
    class function New(const Value: T): IValue<T>; overload;
    function Value: T;
  end;

That anonymous function will be executed only the first time the value is required, and that value will then be cached to be returned in further calls. This is working as expected in all but one situations, described bellow:

private
  FCoordinate: IValue<Double>;

// some other code

function TGeoCoordinate.ToStringValue: IValue<string>;
var
  s: string;
begin
  s := FCoordinate.Value.ToString;
  Result := TValue<string>.New(s);
end;

This code will work perfectly as expected. Since FCoordinate is itself an IValue, FCoordinate.Value will return a Double primitive. So when used in conjuntion with D10's native record helper for Double, FCoordinate.Value.ToStringwill return a primitive string, which s will hold. Then, one of the overloads of TValue<string>.New will accept a primitive string value, and s goes right there in that alley. All fine.

But, that s variable is, or should be, optional. It will only be used once, so we could (should) be able to replace it with the expression itself.

But when I do this:

function TGeoCoordinate.ToStringValue: IValue<string>;
begin
  Result := TValue<string>.New(FCoordinate.Value.ToString);
end;

The compiler will return [dcc32 Error] E2250 There is no overloaded version of 'New' that can be called with these arguments.

While trying to detect the problem, I noticed this will work fine as well:

function TGeoCoordinate.ToStringValue: IValue<string>;
begin
  Result := TValue<string>.New((FCoordinate.Value.ToString));
end;

Adding an extra pair of parentesis to the expression will force it to evaluate the full expression FCoordinate.Value.ToString, and TValue.New will no longer fail.

Nevertheless, I do believe this shouldn't be necessary.

I then tried creating another implementation of IValue, where the class function New is not overloaded, and only accepts a T value, and this also works fine, which means somewhere the compiler is being deceived by the overloaded version of New which accepts an anonymous function.

Could this be a compiler bug, or am I missing something in this picture?

I have tried to search for similar problems, but couldn't fine any search terms that could filter it enough, as this problem uses fairly generic wording.

nunopicado
  • 307
  • 4
  • 17
  • 2
    These is because the Value.ToString can be used in both ways, as a function returning a String or with the Returning Value. Add parentesis to the call like Value.ToString(). Now it is clear what the compiler should use. – Fritzw Jul 26 '17 at 12:45
  • 1
    Rather than *no* overloaded version, it seems more accurate for the compiler to complain that *multiple* overloads could be called. Which overload is called when you add the extra parentheses, I wonder? Which did you *want* to be called? – Rob Kennedy Jul 26 '17 at 12:51
  • That makes sense. ToString is itself a function which complies with the declaration of `TDefineCached = Reference to function: T;`. @RobKennedy is right also, as the error message should state multiple overloads possible. – nunopicado Jul 26 '17 at 12:53
  • @RobKennedy, in this case, the overload of New which gets the value directly was what I was aiming for. – nunopicado Jul 26 '17 at 12:55
  • 3
    That's just the well known consequence of the extended syntax support for calling parameterless functions without using parens. A decision that looks poor today. Solution: use parens to resolve ambiguity. – David Heffernan Jul 26 '17 at 13:03
  • @DavidHeffernan As soon as I commented I realized I should have duped it... didn't take you long! – J... Jul 26 '17 at 13:05
  • 2
    FWIW it seems this defect has been fixed - I can reproduce in 10.0 but not in 10.1 or 10.2. Not the one in the referenced question though as it is slightly different. This issue here is about method overload resolution. – Stefan Glienke Jul 26 '17 at 13:10
  • @StefanGlienke When you say it has been fixed, do you mean that when supplying the method without parens that it correctly resolves the compatible method type and passes it, or do you mean that it resolves to the result value without explicit parens? There are really two issues here, I think - the expectation that the return value should resolve without parens (wrong, by design, and not a bug) and the failure to resolve the compatible method type (ie: `.ToString` is properly `TDefinedCache` probably a defect). – J... Jul 26 '17 at 13:27
  • @StefanGlienke If you mean that new versions correctly resolve `.ToString` as a valid `reference to function : string`, then perhaps something has changed in the way that object methods (or helper methods) are wrapped to anonymous types (ie, for reference : https://stackoverflow.com/a/34681789/327083) – J... Jul 26 '17 at 13:36
  • Execution of right side expression when considering overload resolution always had preference. So, no it does not take the `TDefinedCache` overload. – Stefan Glienke Jul 26 '17 at 13:51
  • @DavidHeffernan: "extended syntax support for calling parameterless functions without using parens"? AFAIK, that was already possible in Wirth's Pascal. I wouldn't call that extended syntax. – Rudy Velthuis Jul 26 '17 at 17:35

0 Answers0